00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 #include "Component/ModelManager.H"
00040 #include "Component/ComponentOpts.H"
00041 #include "Component/ModelOptionDef.H"
00042 #include "Component/EventLog.H"
00043
00044 #include "Image/Image.H"
00045 #include "Raster/Raster.H"
00046 #include "Video/VideoFrame.H"
00047 #include "Util/Types.H"
00048 #include "Util/MathFunctions.H"
00049 #include "Util/FileUtil.H"
00050 #include "Util/sformat.H"
00051 #include "rutz/time.h"
00052
00053 #include "Psycho/PsychoDisplay.H"
00054 #include "Psycho/EyeTrackerConfigurator.H"
00055 #include "Psycho/EyeTracker.H"
00056 #include "Psycho/PsychoOpts.H"
00057 #include "GUI/GUIOpts.H"
00058
00059 #include "Devices/AudioGrabber.H"
00060 #include "Devices/AudioMixer.H"
00061 #include "Devices/DeviceOpts.H"
00062 #include "Audio/AudioWavFile.H"
00063
00064 #include "Media/MPEGStream.H"
00065 #include "Media/MediaOpts.H"
00066 #include "Neuro/NeuroOpts.H"
00067
00068 #include <vector>
00069 #include <pthread.h>
00070 #include <signal.h>
00071
00072
00073
00074
00075
00076 #define CACHELEN 150
00077
00078
00079
00080
00081 const char *imageExtensions[] = { ".pnm", ".pgm", ".ppm", ".pbm", ".pfm", ".png", ".jpeg", ".jpg", ".dpx", NULL };
00082 const char *movieExtensions[] = { ".avi", ".mpg", ".mpeg", ".m4v", ".m2v", ".mov", ".flv", ".dv", NULL };
00083
00084
00085 #define PROC_NORMAL 0
00086 #define PROC_EXP1 1 // online-offline switch
00087 #define PROC_EXP2 2 // scene pairs & online-offline switch
00088 #define PROC_EXP3 3 // change blindness paradigm
00089 #define PROC_EXP4 4 // scene pairs & low-high threshold switch
00090 #define PROC_INVALID -1
00091
00092
00093 #define STIM_IMAGE 0
00094 #define STIM_MOVIE 1
00095 #define STIM_UNKNOWN -1
00096
00097
00098
00099 #define REC_NONE 0
00100 #define REC_DURING (1 << 0)
00101 #define REC_AFTER (1 << 1)
00102 #define REC_ALL (REC_DURING | REC_AFTER)
00103
00104
00105
00106 struct SESSION_EXP12
00107 {
00108 uint index;
00109 uint staticPeriod;
00110 uint blankPeriod;
00111
00112 const char* flag;
00113 const char* message;
00114 };
00115
00116 struct SESSION_EXP3
00117 {
00118 uint index;
00119 uint style;
00120
00121 const char* flag;
00122 };
00123
00124 struct SESSION_EXP4
00125 {
00126 uint index;
00127 uint task;
00128 bool practice;
00129
00130 const char* flag;
00131 const char* reminder;
00132 };
00133
00134
00135
00136
00137
00138 static const ModelOptionDef OPT_ProcedureType =
00139 { MODOPT_ARG_STRING, "ProcedureType", &MOC_DISPLAY, OPTEXP_CORE,
00140 "Use experiment specific procedure types that would override relevant parameter settings.",
00141 "proc-type", '\0', "<Normal|Exp1|Exp2|Exp3|Exp4>", "Normal" };
00142
00143 static const ModelOptionDef OPT_EyeTrackerRecalib =
00144 { MODOPT_ARG(uint), "EyeTrackerRecalibration", &MOC_EYETRACK, OPTEXP_CORE,
00145 "Recalibration frequency of EyeTracker. Set 0 if you don't want recalibration at all."
00146 "If you set to 1, then recalibration will be done after every single image session.",
00147 "et-recalib", '\0', "<int>", "0" };
00148
00149 static const ModelOptionDef OPT_ShuffleOrder =
00150 { MODOPT_FLAG, "ShuffleOrder", &MOC_DISPLAY, OPTEXP_CORE,
00151 "Whether shuffle the order of input stimuli or not.",
00152 "shuffle", '\0', "<bool>", "false" };
00153
00154 static const ModelOptionDef OPT_MouseInput =
00155 { MODOPT_FLAG, "MouseInput", &MOC_DISPLAY, OPTEXP_CORE,
00156 "Make mouse input available and use it instead of key presses.",
00157 "mouse-input", '\0', "<bool>", "false" };
00158
00159 static const ModelOptionDef OPT_BlankPeriod =
00160 { MODOPT_ARG(uint), "BlankPeriod", &MOC_DISPLAY, OPTEXP_CORE,
00161 "The period (in sec) of the blank sessions between the visual stimulus presentations. "
00162 "If set to 0, no blank session will be presented. If set bigger than 999,"
00163 "blank session continues until the user presses mouse button or a key.",
00164 "blank-period", '\0', "<int>", "5" };
00165
00166 static const ModelOptionDef OPT_StaticPeriod =
00167 { MODOPT_ARG(uint), "StaticImagePeriod", &MOC_DISPLAY, OPTEXP_CORE,
00168 "The period (in sec) of static images (if the currently showing image is a raster file) "
00169 "during which they are presented on the screen. For video clips, this option is ignored.",
00170 "static-period", '\0', "<int>", "5" };
00171
00172 static const ModelOptionDef OPT_EyeTrackerRec =
00173 { MODOPT_ARG_STRING, "EyeTrackerRecordStyle", &MOC_EYETRACK, OPTEXP_SAVE,
00174 "During when eye-tracker data should be grabbed and recorded. "
00175 "'During' means the recording is done only during the stimulus presentation whereas "
00176 "'All' is done even after the presentation - i.e. including the blank presentation.",
00177 "et-rec", '\0', "<During|All>", "During" };
00178
00179 static const ModelOptionDef OPT_AudRec =
00180 { MODOPT_ARG_STRING, "AudioRecordStyle", &MOC_DISPLAY, OPTEXP_SAVE,
00181 "During when audio(speech) should be grabbed and recorded. "
00182 "'During' means the recording is done only during the stimulus presentation whereas "
00183 "'After' is done only after the presentation - i.e. during the blank sessions.",
00184 "aud-rec", '\0', "<None|During|After|All>", "All" };
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195 static pthread_mutex_t audMutexKey = PTHREAD_MUTEX_INITIALIZER;
00196 volatile bool audExit = false;
00197 volatile bool audRec = false;
00198
00199 std::vector< AudioBuffer<byte> > rec;
00200
00201
00202
00203
00204
00205
00206 int readRecordStyle( const std::string& recStr )
00207 {
00208 if( strcasecmp( recStr.c_str(), "During" ) == 0 )
00209 return REC_DURING;
00210
00211 if( strcasecmp( recStr.c_str(), "After" ) == 0 )
00212 return REC_AFTER;
00213
00214 if( strcasecmp( recStr.c_str(), "All" ) == 0 )
00215 return REC_ALL;
00216
00217 return REC_NONE;
00218 }
00219
00220
00221 int readProcType( const std::string& procType )
00222 {
00223 if( strcasecmp( procType.c_str(), "Normal" ) == 0 )
00224 return PROC_NORMAL;
00225
00226 if( strcasecmp( procType.c_str(), "Exp1" ) == 0 )
00227 return PROC_EXP1;
00228
00229 if( strcasecmp( procType.c_str(), "Exp2" ) == 0 )
00230 return PROC_EXP2;
00231
00232 if( strcasecmp( procType.c_str(), "Exp3" ) == 0 )
00233 return PROC_EXP3;
00234
00235 if( strcasecmp( procType.c_str(), "Exp4" ) == 0 )
00236 return PROC_EXP4;
00237
00238 return PROC_INVALID;
00239 }
00240
00241
00242 int getStimulusType( const std::string& fname )
00243 {
00244
00245 for( int i = 0; imageExtensions[i]; i ++ )
00246 if( hasExtension( fname, imageExtensions[i] ))
00247 return STIM_IMAGE;
00248
00249
00250 for( int i = 0; movieExtensions[i]; i ++ )
00251 if( hasExtension( fname, movieExtensions[i] ))
00252 return STIM_MOVIE;
00253
00254 return STIM_UNKNOWN;
00255 }
00256
00257
00258 void getStimulusName( const std::string &stimPath, std::string &stimName )
00259 {
00260 std::string name, path;
00261 splitPath( stimPath, path, name );
00262 std::string::size_type dot = name.rfind( '.' );
00263 if( dot != name.npos ) name.erase( dot );
00264
00265 stimName = name;
00266 }
00267
00268
00269 void pause( bool mouse, nub::soft_ref<PsychoDisplay>& d )
00270 {
00271 if( mouse )
00272 d->waitForMouseClick();
00273 else
00274 d->waitForKey();
00275 }
00276
00277
00278 void snooze( uint sec, nub::soft_ref<PsychoDisplay>& d )
00279 {
00280 if( sec < 1 ) return;
00281
00282 rutz::time start = rutz::time::wall_clock_now();
00283 rutz::time stop;
00284
00285 do {
00286 d->checkForKey();
00287 usleep( 50000 );
00288 stop = rutz::time::wall_clock_now();
00289
00290 } while( (uint)(stop-start).sec() < sec );
00291 }
00292
00293
00294 void trackEyes( bool trk, nub::soft_ref<EyeTracker>& et, nub::soft_ref<PsychoDisplay>& d )
00295 {
00296 if( trk )
00297 {
00298 if( !et->isTracking() )
00299 {
00300
00301 et->track( true );
00302
00303
00304 d->displayFixationBlink();
00305 }
00306 } else
00307 {
00308 if( et->isTracking() )
00309 {
00310
00311 usleep( 50000 );
00312 et->track( false );
00313 }
00314 }
00315 }
00316
00317
00318 void recordAudio( bool rec, nub::soft_ref<PsychoDisplay>& d )
00319 {
00320 if( rec )
00321 {
00322 if( !audRec ) d->pushEvent( "---- Audio Recording Start ----" );
00323 audRec = true;
00324 } else
00325 {
00326 if( audRec ) d->pushEvent( "---- Audio Recording Stop ----" );
00327 audRec = false;
00328 }
00329 }
00330
00331
00332 void calibrateISCAN( bool mouse, nub::soft_ref<PsychoDisplay>& d )
00333 {
00334
00335
00336
00337
00338
00339 d->clearScreen();
00340 d->displayText( "ISCAN calibration" );
00341 pause( mouse, d );
00342
00343
00344 d->clearScreen();
00345 d->displayISCANcalib();
00346 pause( mouse, d );
00347
00348
00349 d->clearScreen();
00350
00351 if( mouse )
00352 {
00353 d->displayText( "click LEFT button to calibrate or RIGHT button to skip" );
00354 int ret = d->waitForMouseClick();
00355 if( ret == 1 ) d->displayEyeTrackerCalibration( 3, 5, 1, true );
00356 } else
00357 {
00358 d->displayText( "press SPACE key to calibrate or other key to skip" );
00359 int ret = d->waitForKey();
00360 if( ret == ' ' ) d->displayEyeTrackerCalibration( 3, 5, 1 );
00361 }
00362
00363 d->clearScreen();
00364 }
00365
00366
00367 static bool cacheFrame( nub::soft_ref<InputMPEGStream>& mp, std::deque<VideoFrame>& cache )
00368 {
00369 const VideoFrame frame = mp->readVideoFrame();
00370 if( !frame.initialized() )
00371 return false;
00372
00373 cache.push_front( frame );
00374 return true;
00375 }
00376
00377
00378 void saveAudioRecord( const std::string &wavname, nub::soft_ref<PsychoDisplay>& d )
00379 {
00380 pthread_mutex_lock( &audMutexKey );
00381
00382 if( rec.size() > 0 )
00383 {
00384 LINFO("Saving '%s'...", wavname.c_str());
00385
00386
00387 d->pushEventBegin( std::string("writeAudioFile: '") + wavname + "'" );
00388 writeAudioWavFile( wavname, rec );
00389 d->pushEventEnd( "writeAudioFile" );
00390
00391 rec.clear();
00392 }
00393
00394 pthread_mutex_unlock( &audMutexKey );
00395 }
00396
00397
00398 static void *grabAudio( void *arg )
00399 {
00400 LINFO("Initiating the audio-grabbing thread...");
00401
00402 AudioGrabber *pAgb = (AudioGrabber*)arg;
00403
00404
00405 sigset_t mask;
00406 sigfillset( &mask );
00407 if( pthread_sigmask( SIG_BLOCK, &mask, NULL ) != 0 )
00408 LINFO("Failed to mask signals for the audio-grabbing thread!");
00409
00410
00411 while( !audExit )
00412 {
00413
00414 AudioBuffer<byte> data;
00415 pAgb->grab( data );
00416
00417
00418 if( audRec )
00419 {
00420 pthread_mutex_lock( &audMutexKey );
00421 rec.push_back( data );
00422 pthread_mutex_unlock( &audMutexKey );
00423 }
00424 }
00425
00426 LINFO("Quitting the audio-grabbing thread...");
00427
00428 return NULL;
00429 }
00430
00431
00432
00433
00434
00435
00436 static int submain( const int argc, char** argv )
00437 {
00438 MYLOGVERB = LOG_INFO;
00439
00440
00441 ModelManager manager( "Psycho Narrator" );
00442
00443
00444 OModelParam<std::string> procTypeStr( &OPT_ProcedureType, &manager );
00445 OModelParam<uint> etRecalib( &OPT_EyeTrackerRecalib, &manager );
00446 OModelParam<bool> shuffle( &OPT_ShuffleOrder, &manager );
00447 OModelParam<bool> mouseInput( &OPT_MouseInput, &manager );
00448 OModelParam<uint> blankPeriod( &OPT_BlankPeriod, &manager );
00449 OModelParam<uint> staticPeriod( &OPT_StaticPeriod, &manager );
00450 OModelParam<std::string> audRecStr( &OPT_AudRec, &manager );
00451 OModelParam<std::string> etRecStr( &OPT_EyeTrackerRec, &manager );
00452
00453
00454
00455
00456
00457
00458
00459 nub::soft_ref<PsychoDisplay> d( new PsychoDisplay(manager) );
00460 manager.addSubComponent( d );
00461
00462
00463 nub::soft_ref<EventLog> el( new EventLog(manager) );
00464 manager.addSubComponent( el );
00465
00466
00467 nub::soft_ref<EyeTrackerConfigurator> etc( new EyeTrackerConfigurator(manager) );
00468 manager.addSubComponent( etc );
00469
00470
00471 nub::soft_ref<InputMPEGStream> mp( new InputMPEGStream(manager, "Input MPEG Stream", "InputMPEGStream") );
00472 manager.addSubComponent( mp );
00473
00474
00475 nub::soft_ref<AudioMixer> amx( new AudioMixer(manager) );
00476 manager.addSubComponent( amx );
00477
00478
00479 nub::soft_ref<AudioGrabber> agb( new AudioGrabber(manager) );
00480 manager.addSubComponent( agb );
00481
00482
00483 std::vector< AudioBuffer<byte> > rec;
00484
00485
00486 manager.setOptionValString( &OPT_SDLdisplayDims, "1920x1080" );
00487 manager.setOptionValString( &OPT_EventLogFileName, "narrator.psy" );
00488 manager.setOptionValString( &OPT_EyeTrackerType, "ISCAN" );
00489 manager.setOptionValString( &OPT_InputMPEGStreamPreload, "true" );
00490 manager.setOptionValString( &OPT_AudioMixerLineIn, "false" );
00491 manager.setOptionValString( &OPT_AudioMixerCdIn, "false" );
00492 manager.setOptionValString( &OPT_AudioMixerMicIn, "true" );
00493 manager.setOptionValString( &OPT_AudioGrabberBits, "8" );
00494 manager.setOptionValString( &OPT_AudioGrabberFreq, "11025" );
00495 manager.setOptionValString( &OPT_AudioGrabberBufSamples, "256" );
00496 manager.setOptionValString( &OPT_AudioGrabberChans, "1" );
00497
00498
00499 if( manager.parseCommandLine( argc, argv, "<stimulus 1> ... <stimulus N>", 1, -1 ) == false )
00500 return 1;
00501
00502
00503
00504 int etRecStyle = readRecordStyle( etRecStr.getVal() );
00505 int audRecStyle = readRecordStyle( audRecStr.getVal() );
00506
00507 int procType = readProcType( procTypeStr.getVal() );
00508 if( procType == PROC_INVALID )
00509 LFATAL("Invalid procedure type '%s'", procTypeStr.getVal().c_str());
00510
00511
00512 nub::soft_ref<EyeTracker> et = etc->getET();
00513 d->setEyeTracker( et );
00514 d->setEventLog( el );
00515 et->setEventLog( el );
00516
00517 if( !(audRecStyle & REC_ALL) )
00518 {
00519 manager.removeSubComponent( *amx, true );
00520 manager.removeSubComponent( *agb, true );
00521 }
00522
00523
00524 manager.start();
00525
00526
00527
00528 pthread_t audGrbId;
00529 if( audRecStyle & REC_ALL )
00530 {
00531 pthread_create( &audGrbId, NULL, &grabAudio, (void*)agb.get() );
00532 }
00533
00534
00535
00536
00537
00538
00539 calibrateISCAN( mouseInput.getVal(), d );
00540
00541
00542
00543
00544 switch( procType )
00545 {
00546
00547
00548 case PROC_NORMAL:
00549 LINFO("Normal experiment procedure...");
00550
00551 {
00552 uint recalibCount = 0;
00553 uint stimNum = manager.numExtraArgs();
00554 uint stimIndices[stimNum];
00555 for( uint i = 0; i < stimNum; i ++ ) stimIndices[i] = i;
00556 if( shuffle.getVal() )
00557 {
00558 LINFO("Shuffling stimulus display order...");
00559 randShuffle( stimIndices, stimNum );
00560 }
00561
00562
00563 for( uint i = 0; i < stimNum; i ++ )
00564 {
00565
00566 std::string stimPath = manager.getExtraArg(stimIndices[i]);
00567 std::string stimName;
00568 getStimulusName( stimPath, stimName );
00569 int stimType = getStimulusType( stimPath );
00570 if( stimType == STIM_UNKNOWN )
00571 LFATAL("Unknown stimulus file extension '%s'", stimPath.c_str());
00572
00573 SDL_Surface *surf = NULL;
00574 std::deque<VideoFrame> cache;
00575 bool streaming = true;
00576
00577 d->clearScreen();
00578
00579
00580 if( stimType == STIM_IMAGE )
00581 {
00582
00583 LINFO("Loading '%s'...", stimPath.c_str());
00584 Image< PixRGB<byte> > image = Raster::ReadRGB( stimPath );
00585 surf = d->makeBlittableSurface( image, true );
00586 } else
00587 {
00588
00589 LINFO("Buffering '%s'...", stimPath.c_str());
00590 mp->setFileName( stimPath );
00591 for( uint j = 0; j < CACHELEN; j ++ )
00592 {
00593 streaming = cacheFrame( mp, cache );
00594 if( streaming == false ) break;
00595 }
00596 }
00597 LINFO("'%s' ready.", stimPath.c_str());
00598
00599
00600 snooze( 1, d );
00601 system( "sync" );
00602
00603
00604 d->displayText( sformat( "Session %04d", i + 1 ) );
00605 pause( mouseInput.getVal(), d );
00606
00607 d->waitNextRequestedVsync( false, true );
00608 if( stimType == STIM_IMAGE )
00609 d->pushEvent( std::string("===== Showing image: ") + stimPath + " =====" );
00610 else
00611 d->pushEvent( std::string("===== Playing movie: ") + stimPath + " =====" );
00612
00613
00614 trackEyes( true, et, d );
00615
00616
00617 if( stimType == STIM_IMAGE )
00618 {
00619
00620 d->displaySurface( surf, -2 );
00621
00622
00623 recordAudio( audRecStyle & REC_DURING, d );
00624
00625
00626 snooze( staticPeriod.getVal(), d );
00627
00628 SDL_FreeSurface( surf );
00629
00630 } else
00631 {
00632
00633 d->createVideoOverlay( VIDFMT_YUV420P, mp->getWidth(), mp->getHeight() );
00634
00635
00636 uint frame = 0;
00637 rutz::time start = rutz::time::wall_clock_now();
00638
00639 while( cache.size() )
00640 {
00641 d->checkForKey();
00642
00643
00644 if( streaming )
00645 streaming = cacheFrame( mp, cache );
00646
00647
00648 VideoFrame vidframe = cache.back();
00649 d->displayVideoOverlay( vidframe, frame, SDLdisplay::NEXT_VSYNC );
00650 cache.pop_back();
00651
00652
00653 recordAudio( audRecStyle & REC_DURING, d );
00654
00655 frame ++;
00656 }
00657 rutz::time stop = rutz::time::wall_clock_now();
00658 double secs = (stop - start).sec();
00659 LINFO("%d frames in %.02f sec (~%.02f fps)", frame, secs, frame / secs);
00660
00661
00662
00663
00664
00665 d->destroyYUVoverlay();
00666 d->clearScreen();
00667 }
00668
00669 d->clearScreen();
00670
00671
00672 if( blankPeriod.getVal() > 0 )
00673 {
00674 d->pushEvent( std::string("===== Presenting blank =====") );
00675
00676
00677 trackEyes( etRecStyle & REC_AFTER, et, d );
00678
00679
00680 recordAudio( audRecStyle & REC_AFTER, d );
00681
00682
00683 if( blankPeriod.getVal() < 1000 )
00684 {
00685 snooze( blankPeriod.getVal(), d );
00686 } else
00687 {
00688 pause( mouseInput.getVal(), d );
00689 }
00690 }
00691
00692
00693 trackEyes( false, et, d );
00694
00695
00696 recordAudio( false, d );
00697
00698
00699
00700 saveAudioRecord( stimName + ".wav", d );
00701
00702
00703 if( etRecalib.getVal() > 0 )
00704 {
00705 recalibCount ++;
00706 if( recalibCount == etRecalib.getVal() )
00707 {
00708 recalibCount = 0;
00709 calibrateISCAN( mouseInput.getVal(), d );
00710 }
00711 }
00712 }
00713 }
00714 break;
00715
00716
00717 case PROC_EXP1:
00718 case PROC_EXP2:
00719 if( procType == PROC_EXP1 )
00720 LINFO("Experiment 1 procedure...");
00721 else
00722 LINFO("Experiment 2 procedure...");
00723
00724 {
00725 uint sessionNum;
00726 uint pairNum[2];
00727
00728 if( procType == PROC_EXP1 )
00729 {
00730 sessionNum = manager.numExtraArgs();
00731 } else
00732 {
00733 for( int i = 0; i < 2; i ++ )
00734 {
00735 pairNum[i] = (uint)atoi( manager.getExtraArg(i).c_str() );
00736 if( pairNum[i] < 1 )
00737 LFATAL("Invalid stimulus information '%s'", manager.getExtraArg(i).c_str());
00738 }
00739 sessionNum = pairNum[0] + pairNum[1];
00740 }
00741
00742 SESSION_EXP12 sessions[sessionNum];
00743
00744 if( procType == PROC_EXP1 )
00745 {
00746
00747 for( uint i = 0; i < sessionNum; i += 2 )
00748 {
00749 uint i1, i2;
00750 if( randomUpToNotIncluding( 2 ) )
00751 {
00752 i1 = i;
00753 i2 = i + 1;
00754 } else
00755 {
00756 i1 = i + 1;
00757 i2 = i;
00758 }
00759
00760 sessions[i1].index = i1;
00761 sessions[i1].staticPeriod = 15;
00762 sessions[i1].blankPeriod = 5;
00763 sessions[i1].flag = "ON";
00764 sessions[i1].message = "On-line Description";
00765
00766 sessions[i2].index = i2;
00767 sessions[i2].staticPeriod = 10;
00768 sessions[i2].blankPeriod = 15;
00769 sessions[i2].flag = "OFF";
00770 sessions[i2].message = "Post-scene Description";
00771 }
00772 } else
00773 {
00774
00775 uint idx = 0;
00776 for( int i = 0; i < 2; i ++ )
00777 {
00778 int offset[pairNum[i]];
00779 int task[pairNum[i]];
00780 for( uint j = 0; j < pairNum[i]; j ++ )
00781 {
00782 if( j < pairNum[i] / 2 )
00783 offset[j] = task[j] = 0;
00784 else
00785 offset[j] = task[j] = 1;
00786 }
00787 randShuffle( offset, pairNum[i] );
00788 randShuffle( task, pairNum[i] );
00789
00790 for( uint j = 0; j < pairNum[i]; j ++ )
00791 {
00792 sessions[idx].index = 2 + idx * 2 + offset[j];
00793 if( task[j] == 0 )
00794 {
00795 sessions[idx].staticPeriod = 12;
00796 sessions[idx].blankPeriod = 5;
00797 sessions[idx].flag = "ON";
00798 sessions[idx].message = "Describe as soon as possible";
00799 } else
00800 {
00801 sessions[idx].staticPeriod = 7;
00802 sessions[idx].blankPeriod = 10;
00803 sessions[idx].flag = "OFF";
00804 sessions[idx].message = "Describe in one sentence";
00805 }
00806
00807 idx ++;
00808 }
00809 }
00810 }
00811
00812
00813 randShuffle( sessions, sessionNum );
00814
00815
00816 for( uint i = 0; i < sessionNum; i ++ )
00817 {
00818 d->clearScreen();
00819
00820
00821 std::string stimPath = manager.getExtraArg(sessions[i].index);
00822 std::string stimName;
00823 getStimulusName( stimPath, stimName );
00824 LINFO("Loading '%s'...", stimPath.c_str());
00825 Image< PixRGB<byte> > image = Raster::ReadRGB( stimPath );
00826 SDL_Surface *surf = d->makeBlittableSurface( image, true );
00827 LINFO("'%s' ready.", stimPath.c_str());
00828
00829
00830 snooze( 1, d );
00831 system( "sync" );
00832
00833
00834 d->displayText( sformat( "%s", sessions[i].message ) );
00835 pause( mouseInput.getVal(), d );
00836
00837 d->waitNextRequestedVsync( false, true );
00838 d->pushEvent( std::string("===== Showing image: ") + stimPath + " " + sessions[i].flag + " =====" );
00839
00840
00841 trackEyes( true, et, d );
00842
00843
00844 recordAudio( true, d );
00845
00846
00847 d->displaySurface( surf, -2 );
00848
00849
00850 snooze( sessions[i].staticPeriod, d );
00851
00852 SDL_FreeSurface( surf );
00853 d->clearScreen();
00854
00855
00856 d->pushEvent( std::string("===== Presenting blank =====") );
00857
00858
00859 snooze( sessions[i].blankPeriod, d );
00860
00861
00862 trackEyes( false, et, d );
00863
00864
00865 recordAudio( false, d );
00866
00867
00868 saveAudioRecord( stimName + ".wav", d );
00869 }
00870 }
00871 break;
00872
00873
00874 case PROC_EXP3:
00875 LINFO("Experiment 3 procedure...");
00876
00877 {
00878 uint sessionNum;
00879 uint pairNum[3];
00880
00881 for( int i = 0; i < 3; i ++ )
00882 {
00883 pairNum[i] = (uint)atoi( manager.getExtraArg(i).c_str() );
00884 if( pairNum[i] < 1 )
00885 LFATAL("Invalid stimulus information '%s'", manager.getExtraArg(i).c_str());
00886 }
00887 sessionNum = pairNum[0] + pairNum[1] + pairNum[2];
00888
00889 SESSION_EXP3 sessions[sessionNum];
00890
00891 uint idx = 0;
00892 for( int i = 0; i < 3; i ++ )
00893 {
00894 int offset[pairNum[i]];
00895 for( uint j = 0; j < pairNum[i]; j ++ )
00896 {
00897 if( j < pairNum[i] / 2 )
00898 offset[j] = 0;
00899 else
00900 offset[j] = 1;
00901 }
00902 randShuffle( offset, pairNum[i] );
00903
00904 for( uint j = 0; j < pairNum[i]; j ++ )
00905 {
00906 sessions[idx].style = i;
00907 switch( sessions[idx].style )
00908 {
00909 case 0:
00910 sessions[idx].index = 3 + j;
00911 sessions[idx].flag = "PRACTICE";
00912 break;
00913
00914 case 1:
00915 sessions[idx].index = 3 + pairNum[0] + j * 2;
00916 if( offset[j] == 1 ) sessions[idx].index ++;
00917 sessions[idx].flag = "STD";
00918 break;
00919
00920 case 2:
00921 sessions[idx].index = 3 + pairNum[0] + pairNum[1] * 2 + j * 4;
00922 if( offset[j] == 1 ) sessions[idx].index += 2;
00923 sessions[idx].flag = "CHG";
00924 break;
00925 }
00926
00927 idx ++;
00928 }
00929 }
00930
00931
00932 randShuffle( &sessions[pairNum[0]], sessionNum - pairNum[0] );
00933
00934
00935
00936
00937 uint sessCnt = 1;
00938 uint pracCnt = 1;
00939 for( uint i = 0; i < sessionNum; i ++ )
00940 {
00941 d->clearScreen();
00942
00943
00944 std::string stimPath = manager.getExtraArg(sessions[i].index);
00945 std::string stimName;
00946 getStimulusName( stimPath, stimName );
00947 LINFO("Loading '%s'...", stimPath.c_str());
00948 Image< PixRGB<byte> > image = Raster::ReadRGB( stimPath );
00949 SDL_Surface *surf = d->makeBlittableSurface( image, true );
00950 SDL_Surface *surf_ch = NULL;
00951 if( sessions[i].style == 2 )
00952 {
00953 Image< PixRGB<byte> > image_ch = Raster::ReadRGB( manager.getExtraArg(sessions[i].index + 1) );
00954 surf_ch = d->makeBlittableSurface( image_ch, true );
00955 }
00956 LINFO("'%s' ready.", stimPath.c_str());
00957
00958
00959 snooze( 1, d );
00960 system( "sync" );
00961
00962
00963 if( sessions[i].style == 0 )
00964 d->displayText( sformat( "Pactice %04d", pracCnt ) );
00965 else
00966 d->displayText( sformat( "Session %04d", sessCnt ) );
00967 pause( mouseInput.getVal(), d );
00968
00969 d->displayText( "Describe the event(s) of scene as quickly as possible." );
00970 pause( mouseInput.getVal(), d );
00971
00972 d->waitNextRequestedVsync( false, true );
00973 d->pushEvent( std::string("===== Showing image: ") + stimPath + " " + sessions[i].flag + " =====" );
00974
00975 if( sessions[i].style != 0 )
00976 {
00977
00978 trackEyes( true, et, d );
00979
00980
00981 recordAudio( true, d );
00982 } else
00983 {
00984
00985 d->displayFixationBlink();
00986 }
00987
00988 for( int j = 0; j < 10; j ++ )
00989 {
00990
00991 if( sessions[i].style == 2 && j >= 3 && j <= 6 )
00992 d->displaySurface( surf_ch );
00993 else
00994 d->displaySurface( surf, j == 0 ? -2 : -1 );
00995 usleep( 1000000 );
00996
00997
00998 d->SDLdisplay::clearScreen( PixRGB<byte>(0, 0, 0), true );
00999 usleep( 500000 );
01000
01001 d->checkForKey();
01002 }
01003
01004 SDL_FreeSurface( surf );
01005 if( surf_ch ) SDL_FreeSurface( surf_ch );
01006 d->SDLdisplay::clearScreen( PixRGB<byte>(0, 0, 0), true );
01007
01008
01009 d->pushEvent( std::string("===== Presenting blank =====") );
01010
01011
01012 snooze( 5, d );
01013
01014 if( sessions[i].style != 0 )
01015 {
01016
01017 trackEyes( false, et, d );
01018
01019
01020 recordAudio( false, d );
01021
01022
01023 saveAudioRecord( stimName + ".wav", d );
01024 }
01025
01026 d->displayText( "Describe what you have seen with as much detail as possible." );
01027 pause( mouseInput.getVal(), d );
01028 d->clearScreen();
01029
01030 if( sessions[i].style != 0 )
01031 {
01032
01033 recordAudio( true, d );
01034 }
01035 snooze( 3, d );
01036 pause( mouseInput.getVal(), d );
01037
01038 if( sessions[i].style != 0 )
01039 {
01040
01041 recordAudio( false, d );
01042
01043
01044 saveAudioRecord( stimName + "_post.wav", d );
01045 }
01046
01047 if( sessions[i].style != 0 )
01048 sessCnt ++;
01049 else
01050 pracCnt ++;
01051 }
01052 }
01053 break;
01054
01055
01056 case PROC_EXP4:
01057 LINFO("Experiment 4 procedure...");
01058
01059 {
01060 uint sessionNum;
01061 uint qckSessionNum, frmSessionNum;
01062 uint qckPracNum, frmPracNum;
01063 uint stimNum[3];
01064
01065 for( int i = 0; i < 3; i ++ )
01066 {
01067 stimNum[i] = (uint)atoi( manager.getExtraArg(i).c_str() );
01068 if( stimNum[i] < 1 )
01069 LFATAL("Invalid stimulus information '%s'", manager.getExtraArg(i).c_str());
01070 }
01071 sessionNum = stimNum[0] + stimNum[1] + stimNum[2];
01072 qckSessionNum = (stimNum[0] / 2) + (stimNum[1] / 2) + (stimNum[2] / 2);
01073 frmSessionNum = sessionNum - qckSessionNum;
01074 qckPracNum = stimNum[0] / 2;
01075 frmPracNum = stimNum[0] - qckPracNum;
01076
01077 SESSION_EXP4 sessions[sessionNum];
01078
01079 uint qckIdx = 0;
01080 uint frmIdx = qckSessionNum;
01081 for( int i = 0; i < 3; i ++ )
01082 {
01083 int offset = 3;
01084 for( int j = 0; j < i; j ++ )
01085 offset += stimNum[j];
01086
01087 int task[stimNum[i]];
01088 for( uint j = 0; j < stimNum[i]; j ++ )
01089 task[j] = (j < stimNum[i] / 2) ? 0 : 1;
01090 randShuffle( task, stimNum[i] );
01091
01092 for( uint j = 0; j < stimNum[i]; j ++ )
01093 {
01094 if( task[j] == 0 )
01095 {
01096
01097 sessions[qckIdx].index = j + offset;
01098 sessions[qckIdx].practice = (i == 0);
01099 sessions[qckIdx].task = 0;
01100 sessions[qckIdx].flag = "QCK";
01101 sessions[qckIdx].reminder = "Describe the event(s) of the scene AS QUICKLY AS POSSIBLE while watching.";
01102
01103 qckIdx ++;
01104 } else
01105 {
01106
01107 sessions[frmIdx].index = j + offset;
01108 sessions[frmIdx].practice = (i == 0);
01109 sessions[frmIdx].task = 1;
01110 sessions[frmIdx].flag = "FRM";
01111 sessions[frmIdx].reminder = "Describe the event(s) of the scene in COMPLETE AND WELL-FORMED SENTENCES while watching.";
01112
01113 frmIdx ++;
01114 }
01115 }
01116 }
01117
01118
01119 randShuffle( &sessions[qckPracNum], qckSessionNum - qckPracNum );
01120 randShuffle( &sessions[qckSessionNum + frmPracNum], frmSessionNum - frmPracNum );
01121
01122
01123
01124
01125 uint curTask = 100;
01126 for( uint i = 0; i < sessionNum; i ++ )
01127 {
01128 d->clearScreen();
01129
01130
01131 std::string stimPath = manager.getExtraArg(sessions[i].index);
01132 std::string stimName;
01133 getStimulusName( stimPath, stimName );
01134 LINFO("Loading '%s'...", stimPath.c_str());
01135 Image< PixRGB<byte> > image = Raster::ReadRGB( stimPath );
01136 SDL_Surface *surf = d->makeBlittableSurface( image, true );
01137 LINFO("'%s' ready.", stimPath.c_str());
01138
01139
01140 snooze( 1, d );
01141 system( "sync" );
01142
01143
01144 if( curTask != sessions[i].task )
01145 {
01146 d->displayText( sformat( "Task Description") );
01147 pause( mouseInput.getVal(), d );
01148 switch( sessions[i].task )
01149 {
01150 case 0:
01151 d->displayText( "The task is to describe the event(s) of the scene AS QUICKLY AS POSSIBLE." );
01152 pause( mouseInput.getVal(), d );
01153 d->displayText( "Note that in this task, you don't have to worry about" );
01154 pause( mouseInput.getVal(), d );
01155 d->displayText( "the well-formedness or grammatical correctness of the sentence." );
01156 pause( mouseInput.getVal(), d );
01157 break;
01158
01159 case 1:
01160 d->displayText( "The task is to describe the event(s) of the scene in COMPLETE AND WELL-FORMED SENTENCES." );
01161 pause( mouseInput.getVal(), d );
01162 d->displayText( "Note that in this task, you don't have to speak quickly." );
01163 pause( mouseInput.getVal(), d );
01164 d->displayText( "Focus on the sentence structure while taking as much time as you want." );
01165 pause( mouseInput.getVal(), d );
01166 break;
01167 }
01168 curTask = sessions[i].task;
01169 }
01170
01171
01172 if( sessions[i].practice )
01173 d->displayText( sformat( "Session %04d (Practice)", i + 1 ) );
01174 else
01175 d->displayText( sformat( "Session %04d", i + 1 ) );
01176 pause( mouseInput.getVal(), d );
01177
01178
01179 d->displayText( sessions[i].reminder );
01180 pause( mouseInput.getVal(), d );
01181
01182 d->waitNextRequestedVsync( false, true );
01183 if( sessions[i].practice == false )
01184 {
01185 d->pushEvent( std::string("===== Showing image: ") + stimPath + " " + sessions[i].flag + " =====" );
01186
01187
01188 trackEyes( true, et, d );
01189
01190
01191 recordAudio( true, d );
01192 } else
01193 {
01194 d->pushEvent( std::string("===== Showing image: ") + stimPath + " PRACTICE =====" );
01195
01196
01197 d->displayFixationBlink();
01198 }
01199
01200
01201 d->displaySurface( surf, -2 );
01202
01203
01204 snooze( 3, d );
01205 pause( mouseInput.getVal(), d );
01206
01207 SDL_FreeSurface( surf );
01208 d->clearScreen();
01209
01210 if( sessions[i].practice == false )
01211 {
01212
01213 trackEyes( false, et, d );
01214
01215
01216 recordAudio( false, d );
01217
01218
01219 saveAudioRecord( stimName + ".wav", d );
01220 }
01221
01222
01223 d->pushEvent( std::string("===== Presenting blank =====") );
01224
01225
01226 snooze( 2, d );
01227 }
01228 }
01229 break;
01230
01231
01232 }
01233
01234
01235
01236
01237 d->clearScreen();
01238 d->displayText( "Experiment complete." );
01239 pause( mouseInput.getVal(), d );
01240
01241
01242 audExit = true;
01243 if( audRecStyle & REC_ALL )
01244 pthread_join( audGrbId, NULL );
01245
01246
01247 manager.stop();
01248
01249 return 0;
01250 }
01251
01252
01253
01254
01255
01256
01257
01258 extern "C" int main(const int argc, char** argv)
01259 {
01260
01261
01262
01263
01264
01265 try
01266 {
01267 return submain( argc, argv );
01268 }
01269 catch (...)
01270 {
01271 REPORT_CURRENT_EXCEPTION;
01272 }
01273
01274 return 1;
01275 }
01276