// // Programmer: Craig Stuart Sapp // Programmer: Seny Lee // Creation Date: Mon May 17 10:25:04 PDT 1999 // Last Modified: Tue May 25 20:46:16 PDT 1999 // Last Modified: Fri Jun 25 16:34:54 PDT 1999 // Filename: .../improv/examples/synthImprov/seny/seny.cpp // Syntax: C++; batonSynthImprov 2.1 // #include "batonSynthImprov.h" #include #ifndef OLDCPP #include using namespace std; #else #include #endif #ifdef VISUAL double drand48(void) { return (double)rand()/RAND_MAX; } #endif #define BOUND1 50 /* boundary note of low section of keyboard */ #define BOUND2 79 /* boundary note of high section of keybd */ #define VARIATION 3.0 /* std devs to traverse w/drum controls */ #define MAXVOICES 12 /* maximum drum voices at one time */ #define MINVEL 20 /* minimum attack velocity output */ #define MAXVEL 90 /* maximum attack velocity output */ /*----------------- beginning of improvization algorithms ---------------*/ // Global variables: int activeQ = 1; // true if outputing notes false otherwise int controlDisplayQ = 1; // for displaying baton control variables // variables for keeping track of performer's notes with which baton will // use to generate notes on the piano in an algorithmic fashion. int lastPerformerTime = 0; // time of last performer note on/off int performerNotes[127] = {0}; // all of the performer's notes being played int performerPC[12] = {0}; // pitch class of the performer int performerPCHistory[12] = {0}; // performer pitch class history int keyDuration = 5000; // how long to remember performers notes CircularBuffer keys; // store keys for memory of previous notes CircularBuffer keytimes; // note times for keys buffer // the variables for stick 1 x-axis density control double density = 0; // 0=thick, 1=thin // keep track of performer's notes for stick 1 y-axis register control int performerRegion[3] = {0}; // location that the performer is playing double pregister = 0; // 0=low, 1=high // the variables for stick 1 z-axis controlling consonance and dissonance double consonance = 0.0; // 0 = consonant, 1 = dissonant // cintervals are a list of the intervals in an octave starting with // the most consonant ones and then going to the most dissonant ones. int cintervals[12] = {0,7,5,4,9,8,3,10,2,11,1,6}; // loudness variables for baton stick 2 x-axis control of velocity volumes double avgVol = 0; // average volume double avgVolRange = 0; // standard deviation of the durations CircularBuffer volumes; // duration of notes being held by performer CircularBuffer voltimes; // duration of notes being held by performer int volDuration = 5000; // how long to keep track of volumes double velocity = 0; // 0=similar, 1=inverted // duration variables for baton stick 2 y-axis control of articulations double avgDur = 0; // average duration double avgDurRange = 0; // standard deviation of the durations CircularBuffer durations; // duration of notes being held by performer CircularBuffer durtimes; // duration of notes being held by performer int durDuration = 5000; // how long to keep track of durations double articulation = 0; // 0=staccato, 1=tenuto, 0.5=normal // the variables for stick 2 z-axis controlling rhythm double rhythmType = 0; // 0=constant, 1=random wide variation int zmax = 120; // for z-axis normalizing int zmin = 25; // for z-axis normalizing // baton and buffer checking variables SigTimer batonTimer; // time to get new state of baton SigTimer offTimer; // time to check buffer for forgetting SigTimer controlDisplayTimer; // time to check buffer for forgetting ////////////////////////////// // // Radio Drum performance variables // Voice computer; MidiMessage computerMessage; Voice voice[MAXVOICES]; int voiceState[MAXVOICES] = {0}; int voiceOnTime[MAXVOICES] = {0}; int voiceOffTime[MAXVOICES] = {0}; SigTimer voiceTimer; int voicePeriod = 100; int displayOutput = 0; // for displaying output notes of baton // function declarations: void checkBuffers(void); void checkOffNotes(void); int countActiveVoices(void); void displayVariables(void); void processBaton(void); void processKeyboard(void); void updateDuration(void); void updatePerformRegions(void); void updateVolume(void); int midiLimit(int aNumber); ////////////////////////////// // // Radio Drum performance variables // void generateVoices(void); /*--------------------- maintenance algorithms --------------------------*/ ////////////////////////////// // // description -- this function is called by the improv interface // whenever a capital "D" is pressed on the computer keyboard. // Put a description of the program and how to use it here. // void description(void) { cout << "Seny 1 -- project for \n" " Keyboard commands: \n" " n = display piano's history input pitch set\n" " m = display piano's currently on pitch set\n" " d = display baton's output notes\n" " c = display baton's control variables\n" " r = display piano's register tracking variables\n" " ' ' = activate/deactivate program\n" " v = display active voice count of baton's notes\n" " z = forget the piano's current key history\n" " [ = shorten note memory by 1 second\n" " ] = length note memory by 1 second\n" << endl; } ////////////////////////////// // // initialization -- this function is called by the improv // intervace once at the start of the program. Put items // here which need to be initialized at the beginning of // the program. // void initialization(void) { batonTimer.setPeriod(50); // time to get new state of baton offTimer.setPeriod(200); // time to check buffer for forgetting controlDisplayTimer.setPeriod(200); // time to check buffer for forgetting // set the voice channels all to be 0 for the disklavier and so // the channels do not have to be specified when playing the note. for (int i=0; i 0) { processKeyboard(); } if (offTimer.expired()) { checkBuffers(); checkOffNotes(); offTimer.reset(); } if (batonTimer.expired()) { processBaton(); batonTimer.reset(); } voicePeriod = (int)(avgDur - avgDurRange); if (voicePeriod <= 50) { voicePeriod = 50; } voiceTimer.setPeriod(voicePeriod); if (voiceTimer.expired()) { generateVoices(); voiceTimer.reset(); } if (controlDisplayQ && controlDisplayTimer.expired()) { displayVariables(); controlDisplayTimer.reset(); } } /*-------------------- triggered algorithms -----------------------------*/ /////////////////////////////// // // keyboardchar -- this function is called by the improv interface // whenever a key is pressed on the computer keyboard. // Put commands here which will be executed when a key is // pressed on the computer keyboard. // void keyboardchar(int key) { if (isdigit(key)) { int mkey = 5 * 12 + key - '0'; int mvel = (int)(drand48() * 40 + 40); //turn off old note from computer's point of view computerMessage.time = t_time; computerMessage.p2() = 0; synth.insert(computerMessage); cout << "played note: " << mkey << " with velocity: " << mvel << endl; computer.play(mkey, mvel); computerMessage.time = t_time; computerMessage.p0() = 0x90; // midi note-on command, channel 1 computerMessage.p1() = mkey; computerMessage.p2() = mvel; synth.insert(computerMessage); cout << "played note: " << mkey << " with velocity: " << mvel << endl; return; } switch (key) { case ' ': // turn output notes on/off activeQ = !activeQ; if (activeQ) { cout << "Program Activated" << endl; } else { cout << "Program Deactivated" << endl; } break; case 'c': // toggle baton control display controlDisplayQ = !controlDisplayQ; if (controlDisplayQ == 0) { cout << endl; } break; case 'd': // display baton's output notes displayOutput = !displayOutput; if (displayOutput) { cout << "Baton output notes display turned ON" << endl; } else { cout << "Baton output notes display turned OFF" << endl; } break; case 'n': // display performerPCHistory pitches { cout << "Piano notes: "; for (int i=0; i<11; i++) { cout << performerPCHistory[i] << ", "; } cout << performerPCHistory[11] << endl; } break; case 'm': // display performerPC pitches { cout << "Notes on: "; for (int i=0; i<11; i++) { cout << performerPC[i] << ", "; } cout << performerPC[11] << endl; } break; case 'r': // display the register information { cout << "Register: " << performerRegion[0] << performerRegion[1] << performerRegion[2] << endl; } break; case 'v': // display the active voice count for baton { cout << endl << "Active voices = " << countActiveVoices(); cout << ": "; for (int z=0; z 60000) { keyDuration = 60000; } cout << "Note memory time set to : " << keyDuration/1000 << " seconds" << endl; break; case '-': // turn off computer keyboard note // turn off old note from computer's point of view computerMessage.time = t_time; computerMessage.p2() = 0; synth.insert(computerMessage); // turn off old note from synthesizer's point of view computer.off(); break; } } void stick1trig(void) { generateVoices(); } void stick2trig(void) { generateVoices(); } void b14plustrig(void) { } void b15plustrig(void) { } void b14minusuptrig(void) { } void b14minusdowntrig(void) { } void b15minusuptrig(void) { } void b15minusdowntrig(void) { } /*------------------ begining of assistant functions --------------------*/ ////////////////////////////// // // checkBuffers -- look at the history buffers and determine if any // of the buffers needs to be emptied of any contents. // void checkBuffers(void) { // look at the volume buffers for old volumes: while ( (voltimes.getCount() > 0) && (voltimes[voltimes.getCount()-1] < t_time - volDuration) ) { volumes.extract(); voltimes.extract(); } // look at the duration buffers for old durations: while ( (durtimes.getCount() > 0) && (durtimes[durtimes.getCount()-1] < t_time - durDuration) ) { durations.extract(); durtimes.extract(); } // look at the key buffers for old keys: int oldkey; while ( (keytimes.getCount() > 0) && (keytimes[keytimes.getCount()-1] < t_time - keyDuration) ) { oldkey = keys.extract(); keytimes.extract(); performerPCHistory[oldkey%12]--; if (performerPCHistory[oldkey%12] < 0) { performerPCHistory[oldkey%12] = 0; } } } ////////////////////////////// // // checkOffNotes -- check the voices array to see if any of the notes // need to be turned off. // void checkOffNotes(void) { for (int i=0; i randomval) { break; } } int choosenpc = j; // now that a pitch class from the pianist has been choosen // choose the output pitch class from the radio drum's // consonance dissonance control int outputpc = (choosenpc + cintervals[(int)(consonance*12)]) % 12; // now that we have the output pitch class, assign a register // to it. // choose a register, then look to find if there is a free register // associated with the chosen register int outputregister = (int)(pregister * 3); if (performerRegion[outputregister] != 0) { if (pregister < 0.5) { outputregister = (outputregister+1) % 3; } else { outputregister = (outputregister-1+30) % 3; } } if (performerRegion[outputregister] != 0) { if (pregister < 0.5) { outputregister = (outputregister+1) % 3; } else { outputregister = (outputregister-1+30) % 3; } } if (performerRegion[outputregister] != 0) { if (pregister < 0.5) { outputregister = (outputregister+1) % 3; } else { outputregister = (outputregister-1+30) % 3; } } if (performerRegion[outputregister] != 0) { continue; } int octave = rand()%2 + outputregister * 2 + 3; int outputpitch = outputpc + octave * 12; int initialvolume = (int)(avgVol - VARIATION * avgVolRange + VARIATION * 2 * avgVolRange * velocity); if (initialvolume < MINVEL) { initialvolume = MINVEL; } else if (initialvolume > MAXVEL) { initialvolume = MAXVEL; } // int oppositevol = 127 - initialvolume; // int outputvolume = (int)((oppositevol - initialvolume) * velocity + // initialvolume); int outputvolume = initialvolume; if (outputvolume < MINVEL) { outputvolume = MINVEL; } else if (outputvolume > MAXVEL) { outputvolume = MAXVEL; } // we now know the velocity and pitch of the output note. // now we need to calculate the duration and the voice slot // to place the note in. double outduration = avgDur - VARIATION * avgDurRange + VARIATION * 2 * drand48() * avgDurRange; // adjust duration of notes according to baton control outduration += VARIATION * (avgDurRange * (articulation * 2 - 1)); outduration *= 3.0 * rhythmType; // prevent very short notes, nothing less than 50 ms in duration if (outduration < 50) { outduration = 50.0; } if (avgDur < 50) { outduration = 150.0; } int outputduration = (int)outduration; // choose a voice slot to store the note. int assignment = (int)(drand48() * MAXVOICES); int errorprotect = 0; while (voiceState[assignment] != 0 && errorprotect < MAXVOICES*2) { errorprotect++; assignment = (assignment + 1) % MAXVOICES; } if (voiceState[assignment] == 0 && activeQ) { voiceState[assignment] = 1; voiceOnTime[assignment] = t_time; voiceOffTime[assignment] = t_time + outputduration; voice[assignment].play(outputpitch, outputvolume); if (displayOutput) { cout << "Note: " << outputpitch << "\tvolume = " << outputvolume << "\tduration = " << outputduration << endl; } } } } } ////////////////////////////// // // midiLimit -- guarentee that the given number is in the range // from 0 to 127. // int midiLimit(int aNumber) { if (aNumber < 0) { return 0; } else if (aNumber > 127) { return 127; } else { return aNumber; } } ////////////////////////////// // // processBaton -- update the program variables related to the radio // batons. // void processBaton(void) { // update the variable for keeping track of texture density: // density 0 = thick, density 1 = thin density = baton.x1p/127.0; // baton.x1p == xt1 // update the variable for keeping track of articulation // pregister 0 = low, pregister 1 = high pregister = baton.y1p/127.0; // baton.y1p == yt1 // update the variable for keeping track of the consonance: // consonance 0 = consonant, consonance 1 = dissonant // zt1 = baton.z1p if (zmax - zmin == 0) { consonance = 0.5; } else { consonance = 1.0 - (baton.z1p - zmin) * 1.0 / (zmax - zmin); if (consonance < 0.0) { consonance = 0.0; } } // update the variable for keeping track of velocity // velocity 0 = similar, velocity 1 = inverted velocity = baton.x2p/127.0; // baton.x2p == xt2 // update the variable for keeping track of articulation // articulation 0 = staccato, articulation 1 = tenuto, 0.5 = normal articulation = baton.y2p/127.0; // baton.y2p == yt2 // update the variable for keeping track of rhythm type: // rhythmType 0 = even, rhythmType 1 = random // zt2 = baton.z2p if (zmax - zmin == 0) { rhythmType = 0.5; } else { rhythmType = 1.0 - (baton.z2p - zmin) * 1.0 / (zmax - zmin); } if (rhythmType < 0.0) { rhythmType = 0.0; } } ////////////////////////////// // // processKeyboard -- get notes from the piano and store them as needed // the the radio baton monitoring variables. // void processKeyboard(void) { MidiMessage message; int key; int vel; int command; while (synth.getNoteCount() > 0) { message = synth.extractNote(); command = message.p0(); key = message.p1(); vel = message.p2(); if (vel == 0 || (command & 0xf0 == 0x80)) { // note-off command section long duration = t_time - performerNotes[key]; durations.insert(duration); durtimes.insert(t_time); performerNotes[key] = 0; performerPC[key%12]--; if (performerPC[key%12] < 0) { performerPC[key%12] = 0; } } else { // note-on command performerNotes[key] = t_time; performerPC[key%12]++; performerPCHistory[key%12]++; keys.insert(key); keytimes.insert(t_time); volumes.insert(vel); voltimes.insert(t_time); } // end of the note-on command section } // end of the while loop for processing notes from the performer // update the time that the performer last play a note on/off: lastPerformerTime = t_time; updatePerformRegions(); updateDuration(); updateVolume(); } ////////////////////////////// // // updateDuration -- recalculate the average duration and its possible // range. Calculate average duration and the standard deviation // of durations. // void updatePerformRegions(void) { // determine which region of the piano the performer is // playing on: performerRegion[0] = 0; performerRegion[1] = 0; performerRegion[2] = 0; for (int i=0; i<127; i++) { if (performerNotes[i] > 0) { if (i < BOUND1) { performerRegion[0] = 1; } else if (i < BOUND2) { performerRegion[1] = 1; } else { performerRegion[2] = 1; } } } } ////////////////////////////// // // updateDuration -- recalculate the average duration and its possible // range. Calculate average duration and the standard deviation // of durations. // void updateDuration(void) { int i; double dursum = 0.0; for (i=0; i