//
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
// 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 <craig@ccrma.stanford.edu>, 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]