// // Programmer: Craig Stuart Sapp // Creation Date: Wed Jul 5 09:27:14 PDT 2000 // Last Modified: Sun Jul 9 14:54:38 PDT 2000 // Filename: ...sig/doc/examples/all/tempojnd/tempojnd.cpp // Syntax: C++; synthImprov 2.0 // // Description: This program is used to collect data for // tempo JND experiments. // The experiment is as follows: // A. Play a set of evenly spaced notes at Tempo 1 // B. Play an empty beat (or more) // C. Play a set of evenly spaced notes at Tempo 2 // #include "synthImprov.h" #define NOTE 60 #define VELOCITY 64 #define MINSETA 2 /* Minimum beat count for set A */ #define MINSETB 2 /* Minimum beat count for set B */ #define MINSILENCE 1 /* Minimum spacing between set A and B */ /*----------------- beginning of improvization algorithms ---------------*/ int stage = 0; // 0=off,1=set1,2=silence,3=set2,4=answerwait int laststage = 0; // 0=off,1=set1,2=silence,3=set2,4=answerwait int setAtotal = 10; // number of beats to play in set A int setAcount = 10; // the number of beats at Tempo 1 int setAcurrent = 0; // current location in set A int silencetotal = 1; // number of beats to play in set A int silencecount = 10; // the number of empty beats after set A int silencecurrent = 0; // current location in silence int setBtotal = 10; // number of beats to play in set A int setBcount = 10; // the number of beats at Tempo 2 int setBcurrent = 0; // current location in set B double startTempo; // the start tempo for the experiment double startPeriod; // converted value of startTempo double variation = 1.0; // duration difference from old tempo double nexteventtime = 0; // time to perform another event in milliseconds int runCount = 0; // the trial number int answerCount = 0; // answers for the trials int correctQ = 0; // true if the last answer was correct Voice voice; // for playing notes // function declarations: void performEvent(void); void playbeat(int state); double generateVariation(double lastone, int correct); int randomsign(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 << "TEMPOJND -- Craig Stuart Sapp , July 2000\n" "Commands:\n" " space = initiate a test sequence\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.setPort(synth.getOutputPort()); options.define("t|tempo=d:80", "starting tempo"); options.define("o|output=s:/tmp/tempojnd.dat", "data reporting file"); options.process(); // outputfile = options.getString("output"); startTempo = options.getDouble("tempo"); cout << "Base tempo is: " << startTempo << endl; startPeriod = 60000.0/startTempo; eventIdler.setPeriod(0); // high quality timing srand48(time(NULL) * int(mainTimer.getPeriodCount() * 1000000)); cout << "Press the space bar to begin" << endl; } ////////////////////////////// // // 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) { } /*-------------------- 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 (stage && nexteventtime <= t_time) { performEvent(); } } ////////////////////////////// // // performEvent -- // void performEvent(void) { switch (stage) { case 1: // tempo set A setAcurrent++; if (setAcurrent >= setAtotal) { setAcurrent = 0; stage = 2; } playbeat(1); nexteventtime = nexteventtime + startPeriod; break; case 2: // tempo silence silencecurrent++; if (silencecurrent >= silencetotal) { silencecurrent = 0; stage = 3; } playbeat(0); nexteventtime = nexteventtime + startPeriod; break; case 3: // tempo set B setBcurrent++; if (setBcurrent >= setBtotal) { stage = 4; cout << "\nPress 1 if set B is slower than set A" << endl; cout << "Press 2 if set B is faster than set A" << endl; cout << "Press r to hear the trial again" << endl; } playbeat(1); nexteventtime = nexteventtime + startPeriod + variation; break; case 4: // do nothing: waiting for an answer break; } } ////////////////////////////// // // playbeat -- // void playbeat(int state) { if (state) { voice.play(NOTE, VELOCITY); } else { voice.off(); } } /*-------------------- 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) { switch (key) { case 'r': // replay the test sequence stage = 1; setAcurrent = 0; setBcurrent = 0; silencecurrent = 0; nexteventtime = t_time; cout << "Restarting the trial" << endl; break; case ' ': // start a test sequence if (runCount != answerCount || stage != 0) { cout << "Cannot start a new trial" << endl; break; } else { cout << "Starting trial: " << runCount + 1 << endl; } runCount++; variation = generateVariation(variation, correctQ); cout << "Delta = " << fabs(variation) << " milliseconds " << endl; stage = 1; setAcurrent = 0; setBcurrent = 0; silencecurrent = 0; nexteventtime = t_time; break; case '1': cout << "Set B was chosen as slower than set A" << endl; answerCount++; cout << "Press the spacebar to start a new test" << endl; if (variation > 0) { correctQ = 1; cout << "Correct" << endl; } else { correctQ = 0; cout << "Incorrect" << endl; } nexteventtime = t_time + 1000000; stage = 0; break; case '2': cout << "Set B was chosen as faster than set A" << endl; answerCount++; cout << "Press the spacebar to start a new test" << endl; if (variation < 0) { correctQ = 1; cout << "Correct" << endl; } else { correctQ = 0; cout << "Incorrect" << endl; } stage = 0; nexteventtime = t_time + 1000000; break; case 'q': // length set A setAtotal++; cout << "Beats in set A = " << setAtotal << endl; break; case 'a': // shorten set A setAtotal--; if (setAtotal < MINSETA) { setAtotal = MINSETA; } cout << "Beats in set A = " << setAtotal << endl; break; case 'w': // length set B setBtotal++; cout << "Beats in set B = " << setBtotal << endl; break; case 's': // shorten set B setBtotal--; if (setBtotal < MINSETB) { setBtotal = MINSETB; } cout << "Beats in set B = " << setAtotal << endl; break; } } ////////////////////////////// // // generateVariation -- generate a new tempo variation based on the // last answer -- if correct, then decrease the difference, // otherwise, increase the variation. // double generateVariation(double lastone, int correct) { double increment = 1.0; if (correct) { return randomsign() * (fabs(lastone) - increment); } else { return randomsign() * (fabs(lastone) + increment); } } ////////////////////////////// // // generateVariation -- generate a new tempo variation based on the // last answer -- if correct, then decrease the difference, // otherwise, increase the variation. // int randomsign(void) { if (drand48() < 0.50) { return -1; } else { return 1; } } /*------------------ end improvization algorithms -----------------------*/ // md5sum: 7daf168760d98281896f3cd184b72eac tempojnd.cpp [20050403]