// // Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu> // Creation Date: Sun Jun 3 10:04:21 PDT 2000 // Last Modified: Tue Jul 4 17:11:21 PDT 2000 // Filename: ...sig/doc/examples/all/taptempo/taptempo.cpp // Syntax: C++; synthImprov 2.0 // // Description: A demonstration of various tempo beating algorithms. // which use a binary switch. // #include "synthImprov.h" #include <string.h> /*----------------- beginning of improvization algorithms ---------------*/ #define MAXLAGTIME 70 /* max latency of beat simultaneaity */ // various note algorithms #define GEN_MONO 1 #define GEN_CHROM 2 // various tempo algorithms #define BEAT_AUTO 0 /* computer controlls tempo */ #define BEAT_CONST 1 /* next beat is always constant */ #define BEAT_FIXED 2 /* next beat is always fixed */ #define BEAT_LAST 3 /* next beat is last's beats tempo */ int generation = GEN_CHROM; // note generation algorithm to use int control = BEAT_AUTO; // tempo generation algorithm to use int divisions = 4; // number of notes per beat int divisionChange = 0; // true when divisions change int newdivisions = 4; // new division setting int keyflag = 0; // for options control from computer keyboard int keypresstime = 0; // for elegance of interface CircularBuffer<int> beattime(1000); // time that the last beat occured EventBuffer constcase; // for BEAT_CONST tempo case Voice voice; int running = 0; // 0 = off, 1 = on SigTimer beatlocation; // for keeping track of time double tempo = 60.0; // tempo for fixed tempo algorithms double beatfraction = 0.0; // location in the beat int displayBeatQ = 1; // boolean for displaying subbeat locations // function declarations void beat(void); void checkfornote(double fraction); const char* genString(int generation); const char* tempoString(int tempotype); void playnote(int subbeat, int subcount); void constcaseinsert(int count, double totaldur); void displaySubbeat(int subbeat, int div); /*--------------------- 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 << "TEMPODEMO -- Craig Stuart Sapp <craig@ccrma.stanford.edu> June 2000\n" "Commands:\n" " g# = choose a note generation algorithm\n" " d# = choose a beat division amount\n" " t# = choose a tempo tracking algorithm\n" " b = print the current beat fraction\n" " s = toggle subbeat display\n" " p = toggle periodic tempo display\n" " z = toggle performance on/off\n" " <,> = slow/speed tempo for certain tempo types\n" << endl; } ////////////////////////////// // // initialization -- this function is called by the improv // interface once at the start of the program. Put items // here which need to be initialized at the beginning of // the program. // void initialization(void) { voice.setChannel(0); voice.setPort(synth.getInputPort()); beatlocation.setTempo(tempo); eventIdler.setPeriod(0); constcase.setPollPeriod(10); displayBeatQ = 1; beattime.reset(); beattime[0] = 0; beattime[1] = 0; beattime[2] = 0; beattime[3] = 0; } ////////////////////////////// // // finishup -- this function is called by the improv interface // whenever the program is exited. Put items here which // need to be taken care of when the program is finished. // void finishup(void) { voice.off(); } /*-------------------- main loop algorithms -----------------------------*/ ////////////////////////////// // // mainloopalgorithms -- this function is called by the improv interface // continuously while the program is running. The global variable t_time // which stores the current time is set just before this function is // called and remains constant while in this functions. // void mainloopalgorithms(void) { if (control == BEAT_AUTO) { beatfraction = beatlocation.getPeriodCount() - beatlocation.expired(); } else { beatfraction = beatlocation.getPeriodCount(); } if (running) { if (control == BEAT_CONST) { constcase.checkPoll(); } else { checkfornote(beatfraction); } } else { beatfraction = -1; } } ////////////////////////////// // // checkfornote -- determine if it is time to play a new note // based on the beat location. // void checkfornote(double fraction) { // always wait for the next beat to occur if (fraction > 1.0) { return; } static double lastfraction = 0.0; static int lastnote = -1; static int subbeat = 0; if (fraction < lastfraction && divisions == 1 && control != BEAT_CONST) { playnote(0, 1); lastfraction = fraction; displaySubbeat(0, divisions); if (divisionChange) { divisions = newdivisions; divisionChange = 0; } return; } if (control == BEAT_CONST) { lastfraction = fraction; return; } if (fraction >= 1.0 && control == BEAT_FIXED) { lastfraction = fraction; return; } subbeat = (int)(fraction * divisions); if (lastnote != subbeat) { if (subbeat == 0 && divisionChange) { divisions = newdivisions; divisionChange = 0; } if (subbeat == 0 && lastnote != divisions - 1) { for (int j=0; j<divisions - 1 - lastnote; j++) { playnote(lastnote + j + 1, divisions); displaySubbeat(lastnote + j + 1, divisions); } } playnote(subbeat, divisions); lastnote = subbeat; displaySubbeat(subbeat, divisions); } if (subbeat > divisions) { subbeat = divisions - 1; } if (lastnote > divisions) { lastnote = divisions - 1; } lastfraction = fraction; } void displaySubbeat(int subbeat, int div) { if (displayBeatQ == 0) { return; } if (subbeat == 0) { cout << endl; } else { cout << " "; } cout << subbeat << flush; } /*-------------------- triggered algorithms -----------------------------*/ /* uncomment these line to use with the Radio Baton void stick1trig(void) { beat(); } void stick2trig(void) { beat(); } void b14plustrig(void) { beat(); } void b15plustrig(void) { beat(); } void b14minusuptrig(void) { beat(); } void b14minusdowntrig(void) { beat(); } void b15minusuptrig(void) { beat(); } void b15minusdowntrig(void) { beat(); } */ /////////////////////////////// // // 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) { keypresstime = t_time; switch (key) { case 'b': // print the current beat fraction cout << "Beat fraction = " << beatfraction << endl; break; case 's': // toggle showing of subbeat locations displayBeatQ = !displayBeatQ; if (displayBeatQ) { cout << "Subbeat display is turned ON" << endl; } else { cout << "Subbeat display is turned OFF" << endl; } break; case 'z': // toggle performance running = !running; if (running) { cout << "Performing" << endl; beatlocation.reset(); } else { cout << "Performance stopped" << endl; } break; case ' ': beat(); break; case 'g': cout << "Choose a number between 1 and 9 to control note algorithm" << endl; keyflag = 'g'; break; case 'd': cout << "Choose a number between 1 and 9 to select beat division" << endl; keyflag = 'd'; break; case 't': cout << "Choose a number between 1 and 9 to control tempo algorithm" << endl; keyflag = 't'; break; case '<': // slow the tempo down for auto/fixed/const tempo options case ',': // slow the tempo down for auto/fixed/const tempo options tempo /= 1.02; beatlocation.setTempo(tempo); cout << "Fixed tempo set to: " << tempo << endl; break; case '>': // speed up the tempo down for auto/fixed/const tempo options case '.': // speed up the tempo down for auto/fixed/const tempo options tempo *= 1.02; beatlocation.setTempo(tempo); cout << "Auto/const/fixed tempo set to: " << tempo << endl; break; } if (isdigit(key)) { switch (keyflag) { case 'g': generation = key - '0'; // note generation algorithm to use cout << "Generation algorithm is: " << genString(generation) << endl; break; case 'd': if (key == '0') { break; } if (keypresstime - beattime[0] < MAXLAGTIME) { divisions = key - '0'; // number of notes per beat cout << "Time diff: " << keypresstime - beattime[0] << endl; cout << "Beat division: " << divisions << endl; divisionChange = 0; } else { newdivisions = key - '0'; // number of notes per beat cout << "Time diff: " << keypresstime - beattime[0] << endl; cout << "Beat division: " << divisions << endl; divisionChange = 1; } break; case 't': control = key - '0'; // tempo generation algorithm to use cout << "Tempo algorithm is: " << tempoString(control) << endl; break; case 'q': // quiet running = 0; break; default: keyflag = 0; } } } /*------------------ end improvization algorithms -----------------------*/ ////////////////////////////// // // beat -- cause a new beat to occur. // void beat(void) { beattime.insert(t_time); beatlocation.reset(); if (running == 0) { running = 1; beatlocation.reset(); } if (control == BEAT_CONST) { constcaseinsert(divisions, beatlocation.getPeriod()); return; } int diff = beattime[0] - beattime[1]; // ignore initial beats if (diff > 5000) { return; } return; beatlocation.setPeriod(diff); } ////////////////////////////// // // genString -- print the note generation algorithm type. // const char* genString(int generation) { static char string[128] = {0}; switch (generation) { case GEN_MONO: strcpy(string, "Unison"); break; case GEN_CHROM: strcpy(string, "Chromatic"); break; default: strcpy(string, "Unknown"); break; } return string; } ////////////////////////////// // // playnote -- play notes for beating // void playnote(int subbeat, int subcount) { if (control == BEAT_AUTO && subbeat == 0) { beattime.insert(t_time); } if (subbeat == subcount) { cout << "Illegal beat: " << subbeat << "\tdivisions = " << subcount << endl; } voice.play(60 + subbeat, 64); } ////////////////////////////// // // tempoString -- print the note generation algorithm type. // const char* tempoString(int tempotype) { static char string[128] = {0}; switch (tempotype) { case BEAT_AUTO: strcpy(string, "Auto Tempo"); break; case BEAT_CONST: strcpy(string, "Const Tempo"); break; case BEAT_FIXED: strcpy(string, "Fixed Tempo"); break; case BEAT_LAST: strcpy(string, "Last Beat-duration Tempo"); break; default: strcpy(string, "Unknown"); break; } return string; } ////////////////////////////// // // constcaseinsert -- add all notes // void constcaseinsert(int count, double totaldur) { static NoteEvent note; // temporary note for placing in buffer double starttime = mainTimer.getTime(); double duration; if (count > 0) { duration = totaldur / count; } else { duration = 0.0; } int basenote = 60; int i; for (i=0; i<count; i++) { note.setOnDur((int)starttime, (int)duration); note.setKey(basenote + i); note.setVel(64); note.setChan(0); note.activate(); constcase.insert(note); starttime += duration; } } // md5sum: b1cbaa2e0800e5e5e10f804ebd01e97f taptempo.cpp [20050403]