00001 // VisageLinkDoc.cpp : implementation of the CVisageLinkDoc class 00002 // 00003 00004 #include "stdafx.h" 00005 #include "VisageLink.h" 00006 00007 #include "VisageLinkDoc.h" 00008 00009 /* 00010 #ifdef _DEBUG 00011 #define new DEBUG_NEW 00012 #undef THIS_FILE 00013 static char THIS_FILE[] = __FILE__; 00014 #endif 00015 */ 00016 00017 #include "OpenGLWnd.h" 00018 #include "math.h" 00019 #include "MainFrm.h" 00020 #include "SimpleFacialExpression.h" 00021 #include "SimpleFbaAction.h" 00022 00023 00024 ///////////////////////////////////////////////////////////////////////////// 00025 // CVisageLinkDoc 00026 00027 IMPLEMENT_DYNCREATE(CVisageLinkDoc, CDocument) 00028 00029 BEGIN_MESSAGE_MAP(CVisageLinkDoc, CDocument) 00030 //{{AFX_MSG_MAP(CVisageLinkDoc) 00031 ON_COMMAND(ID_FILE_START, OnFileStart) 00032 ON_COMMAND(ID_FILE_SPEAK, OnFileSpeak) 00033 ON_COMMAND(ID_OPTIONS_GAZEFOLLOWINGVOR, OnOptionsGazefollowingvor) 00034 ON_COMMAND(ID_OPTIONS_DISABLEGAZEFOLLOWINGVOR, OnOptionsDisablegazefollowingvor) 00035 //}}AFX_MSG_MAP 00036 END_MESSAGE_MAP() 00037 00038 ///////////////////////////////////////////////////////////////////////////// 00039 // CVisageLinkDoc construction/destruction 00040 00041 CVisageLinkDoc::CVisageLinkDoc() 00042 { 00043 animationStarted = false; 00044 m_FAPlayer = new FAPlayer(); 00045 vor = new SimpleVORAction(); 00046 } 00047 00048 CVisageLinkDoc::~CVisageLinkDoc() 00049 { 00050 } 00051 00052 BOOL CVisageLinkDoc::OnNewDocument() 00053 { 00054 if (!CDocument::OnNewDocument()) 00055 return FALSE; 00056 00057 // TODO: add reinitialization code here 00058 // (SDI documents will reuse this document) 00059 00060 return TRUE; 00061 } 00062 00063 BOOL CVisageLinkDoc::OnOpenDocument(LPCTSTR lpszPathName) 00064 { 00065 return TRUE; 00066 } 00067 00068 00069 00070 ///////////////////////////////////////////////////////////////////////////// 00071 // CVisageLinkDoc serialization 00072 00073 void CVisageLinkDoc::Serialize(CArchive& ar) 00074 { 00075 if (ar.IsStoring()) 00076 { 00077 // TODO: add storing code here 00078 } 00079 else 00080 { 00081 // TODO: add loading code here 00082 } 00083 } 00084 00085 00086 00087 /** 00088 * A simple wave action. 00089 * This is an implementation of an extremely simple hand waving action, actually just a very basic movement of the 00090 * right arm. 00091 */ 00092 class SimpleWaveAction : public SimpleFbaAction 00093 { 00094 public: 00095 FBAPs *getFBAPs(long globalTime, FBAPs *lastFBAPs, AFM *afm) 00096 { 00097 long localTime = globalTime - globalStartTime; 00098 float angle = 0.0f; 00099 00100 00101 if(!fbaps) 00102 fbaps = new FBAPs(); 00103 00104 00105 angle = -sin((1000+localTime)/300.0f) * 300000; 00106 00107 fbaps->setBAP(r_shoulder_flexion,(int)angle); 00108 00109 if(localTime < 500) 00110 return(fbaps); 00111 else 00112 return NULL; 00113 }; 00114 }; 00115 00116 00117 /** 00118 * Processing of the TTS viseme notification events. 00119 * This function is the implementation of a virtual function from the Visagesapi5ttsObserver class. 00120 * It is not to be called directly. Rather, the instance of CVisageLinkDoc is attached to the 00121 * Visagesapi5tts, which calls the Notify() function as appropriate, and passes the appropriate 00122 * parameters to it. 00123 * 00124 * This gives the possibility of additional control over the animation parameters generated by TTS. 00125 * In this example we do not use it so it is empty. 00126 * 00127 * @param faps pointer to FAPs structure, or NULL at the end of speech. 00128 * @param cp pointer to the coding parameters used when encoding the animation into a file; 00129 * @param pts current TTS speech bit stream time in milliseconds. 00130 * @param vtts instance of Visagesapi5tts that called the function; this can be used for identification if more than one Visagesapi5tts object is used at the same time 00131 * 00132 * @see Visagesapi5ttsObserver 00133 * @see preRender() 00134 */ 00135 void CVisageLinkDoc::Notify (Visagesapi5tts *vtts, FBAPs *fbaps, CodingParameters *cp, long pts) 00136 { 00137 } 00138 00139 /** 00140 * Processing of the TTS bookmark events. 00141 * This function is the implementation of a virtual function from the Visagesapi5ttsObserver class. 00142 * It is not to be called directly. Rather, the instance of CVisageLinkDoc is attached to the 00143 * Visagesapi5tts, which calls the BookMark() function as appropriate, and passes the appropriate 00144 * parameters to it. 00145 * 00146 * In this example we use the bookmarks to insert simple facial expressions or animations at particular times 00147 * during the speech. This is done by playing a SimpleFacialExpression. The expression is 00148 * initialized with the name from the bookmark tag, e.g. "surprise" or "joy". See SimpleFacialExpression 00149 * for the list of supported expression names. 00150 * 00151 * The other bookmark that can occur is "wave", and if it is found we insert a simple waving animation - just moving the right hand. 00152 * 00153 * @param qTimeStamp bookmark time stamp in milliseconds. This is bookmark's TTS speech bit stream time. 00154 * @param dwMarkNum bookmark code number (the number that was entered in the SAPI control tag as \\Mrk=number\. 00155 * @param vtts instance of Visagesapi5tts that called the function; this can be used for identification if more than one Visagesapi5tts object is used at the same time 00156 * 00157 * @see Visagesapi5ttsObserver 00158 * @see SimpleFacialExpression 00159 */ 00160 void CVisageLinkDoc::BookMark (Visagesapi5tts *vtts, long qTimeStamp, DWORD dwMarkNum, WCHAR *markText) 00161 { 00162 CString t(markText); 00163 if(!t.CompareNoCase("wave")) 00164 { 00165 SimpleWaveAction *wave = new SimpleWaveAction(); 00166 m_FAPlayer->playTrack(wave); 00167 } 00168 else 00169 { 00170 SimpleFacialExpression *exp = new SimpleFacialExpression((char*)markText,2200,1.3f); 00171 m_FAPlayer->playTrack(exp); 00172 } 00173 } 00174 00175 /** 00176 * Processing of the TTS start speech events. 00177 * This function is the implementation of a virtual function from the Visagesapi5ttsObserver class. 00178 * It is not to be called directly. Rather, the instance of CVisageLinkDoc is attached to the 00179 * Visagesapi5tts, which calls the StartSpeech() function as appropriate, and passes the appropriate 00180 * parameters to it. 00181 * 00182 * In this example we use the bookmarks to insert simple expressions and animations at particular times 00183 * during the speech. Some bookmarks are already inserted in the input text file. We use this function 00184 * as a simple filter to insert additional bookmarks. 00185 * 00186 * So, when the string "surpr" is encountered, a bookmark for the surprize expression is inserted. 00187 * 00188 * This is a very simple example of using the Visagesapi5ttsObserver for processing the text and 00189 * inserting appropriate actions. 00190 * 00191 * @param qTimeStamp bookmark time stamp in milliseconds. This is bookmark's TTS speech bit stream time. 00192 * @param dwMarkNum bookmark code number (the number that was entered in the SAPI control tag as \\Mrk=number\. 00193 * @param vtts instance of Visagesapi5tts that called the function; this can be used for identification if more than one Visagesapi5tts object is used at the same time 00194 * 00195 * @see Visagesapi5ttsObserver 00196 * @see SimpleFacialExpression 00197 */ 00198 void CVisageLinkDoc::StartSpeech (Visagesapi5tts *vtts, WCHAR *text) 00199 { 00200 CString t(text); 00201 int ind = 0; 00202 while(ind != -1) 00203 { 00204 ind = t.Find("surpr",ind); 00205 if(ind != -1) 00206 { 00207 t.Insert(ind,"<BOOKMARK MARK='surprise'/>"); 00208 ind += 32; 00209 } 00210 } 00211 00212 00213 ind = t.GetLength(); 00214 00215 char *text1 = t.GetBuffer(ind); 00216 00217 Visagesapi5tts::toUnicode(text,text1); 00218 } 00219 00220 ///////////////////////////////////////////////////////////////////////////// 00221 // CVisageLinkDoc diagnostics 00222 00223 #ifdef _DEBUG 00224 void CVisageLinkDoc::AssertValid() const 00225 { 00226 CDocument::AssertValid(); 00227 } 00228 00229 void CVisageLinkDoc::Dump(CDumpContext& dc) const 00230 { 00231 CDocument::Dump(dc); 00232 } 00233 #endif //_DEBUG 00234 00235 ///////////////////////////////////////////////////////////////////////////// 00236 // CVisageLinkDoc commands 00237 00238 /** 00239 * Check if all necessary files are present. 00240 * 00241 * All the file names in this example are hard-coded. This function checks if the files are actually 00242 * present and gives error messages if they are not. The example should be run in the VisageLink folder where all necessary files are present. 00243 */ 00244 void CVisageLinkDoc::CheckFiles() 00245 { 00246 FILE * testFP; 00247 00248 if(!(testFP = fopen("p2v.cfg","r"))) 00249 { 00250 CString msg; 00251 msg = "The working folder does not contain the file p2v.cfg, necessary for the TTS. This file can be found in the visageSDK/lib folder, and also in the visageSDK/Samples/VisageLink folder. It is best to run this example in visageSDK/Samples/VisageLink folder."; 00252 MessageBox(0,msg,"Error",MB_ICONERROR); 00253 exit(1); 00254 } 00255 else fclose(testFP); 00256 00257 00258 00259 if(!(testFP = fopen("test-sapi5-expression.txt","r"))) 00260 { 00261 CString msg; 00262 msg = "The working folder does not contain the example file test-sapi5-expression.txt, necessary to run this example. This file can be found in the visageSDK/Samples/VisageLink folder. It is best to run this example in visageSDK/Samples/VisageLink folder."; 00263 MessageBox(0,msg,"Error",MB_ICONERROR); 00264 exit(1); 00265 } 00266 else fclose(testFP); 00267 00268 00269 if(!(testFP = fopen("dubravka.afm","r"))) 00270 { 00271 CString msg; 00272 msg = "The working folder does not contain the file dubravka.afm, necessary to run this example. This file can be found in the visageSDK/Samples/VisageLink folder. It is best to run this example in visageSDK/Samples/VisageLink folder."; 00273 MessageBox(0,msg,"Error",MB_ICONERROR); 00274 exit(1); 00275 } 00276 else fclose(testFP); 00277 00278 if(!(testFP = fopen("headmotion_short.fba","r"))) 00279 { 00280 CString msg; 00281 msg = "The working folder does not contain the file headmotion_short.fba, necessary to run this example. This file can be found in the visageSDK/Samples/VisageLink folder. It is best to run this example in visageSDK/Samples/VisageLink folder."; 00282 MessageBox(0,msg,"Error",MB_ICONERROR); 00283 exit(1); 00284 } 00285 else fclose(testFP); 00286 } 00287 00288 /** 00289 * Initialize everything and start the animation. 00290 * 00291 * This method is called when the File->Start menu option is selected. It performs all necessary initializations, and starts the FAPlayer. 00292 * 00293 * Specifically: 00294 * - a check is performed to see if all required files are present 00295 * - a face/body model is loaded and attached to the FAPlayer 00296 * - a base animation track (simple head motion and eye blinks) is loaded from file into the player; it will loop forever. This makes the animation look nicer, and provides some basic action to the FAPlayer so that it continues playing (if the player is empty, the play() method would just return because there is nothing to play) 00297 * - the specch synthesis object vtts is initialised and added as an animation track to the player 00298 * - the rendering mechanism is started 00299 * - the FAPlayer play() method is called to start the player. 00300 */ 00301 void CVisageLinkDoc::OnFileStart() 00302 { 00303 00304 00305 if(animationStarted) // If animation is already started do nothing. 00306 return; 00307 00308 animationStarted = true; 00309 00310 CheckFiles(); // check if all necessary files are present 00311 00312 AFM *afm = new AFM("dubravka.afm"); // load the face/body model 00313 m_FAPlayer->init(afm); // add the model to the player, and add this CVisageLinkDoc as renderer 00314 // m_FAPlayer->setSleepTimePerCycle(100); 00315 00316 FbaFileAction *trk = m_FAPlayer->addTrack("headmotion_short.fba", 1, 0); // start playing the base animation 00317 trk->smoothZeroValues(); // smooth the zero values to avoid jerks when merging with other actions 00318 00319 vtts = new Visagesapi5tts("."); // construct a TTS 00320 vtts->setSpeaker("Mary"); // set a female voice 00321 m_FAPlayer->addTrack(vtts);// add vtts as an animation track to the FAPlayer 00322 vtts->attach(this); // attach this CVisageLinkDoc to the vtts as observer; 00323 // it will get viseme and bookmark events; 00324 // bookmark events are used to insert expressions 00325 00326 // get the renderer 00327 CMainFrame* pMainWnd = (CMainFrame*)AfxGetMainWnd(); 00328 COpenGLWnd *pOpenGL = pMainWnd->GetOpenGLWnd(); 00329 00330 // initialise the renderer with the new face model 00331 pOpenGL->Init(m_FAPlayer->getFaceModel()); 00332 00333 m_FAPlayer->play(); // start the player. 00334 } 00335 00336 /** 00337 * Speak. 00338 * 00339 * This method is called when the File->Speak menu option is selected. It reads the speech from the file and pronounces it using the speech synthesis object vtts. 00340 */ 00341 void CVisageLinkDoc::OnFileSpeak() 00342 { 00343 if(!animationStarted) // if the animation is not initialized, we can not speak yet 00344 return; 00345 00346 FILE *fp; 00347 char s[2000]; 00348 00349 fp = fopen("test-sapi5-expression.txt","r"); // open the file with the speech to pronounce 00350 fgets(s,2000,fp); // read the text from the file (only the first line is treated) 00351 fclose(fp); // close the file 00352 00353 00354 vtts->speak(s,1); // speak 00355 } 00356 00357 /** 00358 * Enable gaze following (vestibulo-ocular reflex). 00359 * 00360 * This method is called when the File->Enable Gaze Following menu option is selected. 00361 * 00362 * It enables the gaze following (vestibulo-ocular reflex) by attaching the vor object (a SimpleVORAction) 00363 * to the FAPlayer. 00364 * 00365 * @see SimpleVORAction 00366 */ 00367 void CVisageLinkDoc::OnOptionsGazefollowingvor() 00368 { 00369 if(!vor) return; 00370 if(!m_FAPlayer) return; 00371 00372 m_FAPlayer->addTrack(vor); 00373 } 00374 00375 /** 00376 * Disable gaze following (vestibulo-ocular reflex). 00377 * 00378 * This method is called when the File->Disable Gaze Following menu option is selected. 00379 * 00380 * It disables the gaze following (vestibulo-ocular reflex) by removing the vor object (a SimpleVORAction) 00381 * from the FAPlayer. 00382 * 00383 * @see SimpleVORAction 00384 */ 00385 void CVisageLinkDoc::OnOptionsDisablegazefollowingvor() 00386 { 00387 if(!vor) return; 00388 if(!m_FAPlayer) return; 00389 00390 m_FAPlayer->removeTrack(vor); 00391 }