Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

8796 lines
190 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include <Assert.h>
  8. #include <stdio.h>
  9. #include <math.h>
  10. #include "hlfaceposer.h"
  11. #include "PhonemeEditor.h"
  12. #include "PhonemeEditorColors.h"
  13. #include "snd_audio_source.h"
  14. #include "snd_wave_source.h"
  15. #include "ifaceposersound.h"
  16. #include "choreowidgetdrawhelper.h"
  17. #include "mxBitmapButton.h"
  18. #include "phonemeproperties.h"
  19. #include "tier2/riff.h"
  20. #include "StudioModel.h"
  21. #include "expressions.h"
  22. #include "expclass.h"
  23. #include "InputProperties.h"
  24. #include "phonemeextractor/PhonemeExtractor.h"
  25. #include "PhonemeConverter.h"
  26. #include "choreoevent.h"
  27. #include "choreoscene.h"
  28. #include "ChoreoView.h"
  29. #include "FileSystem.h"
  30. #include "UtlBuffer.h"
  31. #include "AudioWaveOutput.h"
  32. #include "StudioModel.h"
  33. #include "viewerSettings.h"
  34. #include "ControlPanel.h"
  35. #include "faceposer_models.h"
  36. #include "tier1/strtools.h"
  37. #include "tabwindow.h"
  38. #include "MatSysWin.h"
  39. #include "soundflags.h"
  40. #include "mdlviewer.h"
  41. #include "filesystem_init.h"
  42. #include "WaveBrowser.h"
  43. #include "tier2/p4helpers.h"
  44. #include "vstdlib/random.h"
  45. extern IUniformRandomStream *random;
  46. float SnapTime( float input, float granularity );
  47. #define MODE_TAB_OFFSET 20
  48. // 10x magnification
  49. #define MAX_TIME_ZOOM 1000
  50. // 10% per step
  51. #define TIME_ZOOM_STEP 2
  52. #define SCRUBBER_HEIGHT 15
  53. #define TAG_TOP ( 25 + SCRUBBER_HEIGHT )
  54. #define TAG_BOTTOM ( TAG_TOP + 20 )
  55. #define PLENTY_OF_TIME 99999.9
  56. #define MINIMUM_WORD_GAP 0.02f
  57. #define MINIMUM_PHONEME_GAP 0.01f
  58. #define DEFAULT_WORD_LENGTH 0.25f
  59. #define DEFAULT_PHONEME_LENGTH 0.1f
  60. #define WORD_DATA_EXTENSION ".txt"
  61. // #define ITEM_GAP_EPSILON 0.0025f
  62. struct PhonemeEditorColor
  63. {
  64. int color_number; // For readability
  65. int mode_number; // -1 for all
  66. Color root_color;
  67. Color gray_color; // if mode is wrong...
  68. };
  69. static PhonemeEditorColor g_PEColors[ NUM_COLORS ] =
  70. {
  71. { COLOR_PHONEME_BACKGROUND, -1, Color( 240, 240, 220 ) },
  72. { COLOR_PHONEME_TEXT, -1, Color( 63, 63, 63 ) },
  73. { COLOR_PHONEME_LIGHTTEXT, 0, Color( 180, 180, 120 ) },
  74. { COLOR_PHONEME_PLAYBACKTICK, 0, Color( 255, 0, 0 ) },
  75. { COLOR_PHONEME_WAVDATA, 0, Color( 128, 31, 63 ) },
  76. { COLOR_PHONEME_TIMELINE, 0, Color( 31, 31, 127 ) },
  77. { COLOR_PHONEME_TIMELINE_MAJORTICK, 0, Color( 200, 200, 255 ) },
  78. { COLOR_PHONEME_TIMELINE_MINORTICK, 0, Color( 210, 210, 240 ) },
  79. { COLOR_PHONEME_EXTRACTION_RESULT_FAIL, 0, Color( 180, 180, 0 ) },
  80. { COLOR_PHONEME_EXTRACTION_RESULT_SUCCESS, 0, Color( 100, 180, 100 ) },
  81. { COLOR_PHONEME_EXTRACTION_RESULT_ERROR, 0, Color( 255, 31, 31 ) },
  82. { COLOR_PHONEME_EXTRACTION_RESULT_OTHER, 0, Color( 63, 63, 63 ) },
  83. { COLOR_PHONEME_TAG_BORDER, 0, Color( 160, 100, 100 ) },
  84. { COLOR_PHONEME_TAG_BORDER_SELECTED, 0, Color( 255, 40, 60 ) },
  85. { COLOR_PHONEME_TAG_FILLER_NORMAL, 0, Color( 210, 210, 190 ) },
  86. { COLOR_PHONEME_TAG_SELECTED, 0, Color( 200, 130, 130 ) },
  87. { COLOR_PHONEME_TAG_TEXT, 0, Color( 63, 63, 63 ) },
  88. { COLOR_PHONEME_TAG_TEXT_SELECTED, 0, Color( 250, 250, 250 ) },
  89. { COLOR_PHONEME_WAV_ENDPOINT, 0, Color( 0, 0, 200 ) },
  90. { COLOR_PHONEME_AB, 0, Color( 63, 190, 210 ) },
  91. { COLOR_PHONEME_AB_LINE, 0, Color( 31, 150, 180 ) },
  92. { COLOR_PHONEME_AB_TEXT, 0, Color( 100, 120, 120 ) },
  93. { COLOR_PHONEME_ACTIVE_BORDER, 0, Color( 150, 240, 180 ) },
  94. { COLOR_PHONEME_SELECTED_BORDER, 0, Color( 255, 0, 0 ) },
  95. { COLOR_PHONEME_TIMING_TAG, -1, Color( 0, 100, 200 ) },
  96. { COLOR_PHONEME_EMPHASIS_BG, 1, Color( 230, 230, 200 ) },
  97. { COLOR_PHONEME_EMPHASIS_BG_STRONG, 1, Color( 163, 201, 239 ) },
  98. { COLOR_PHONEME_EMPHASIS_BG_WEAK, 1, Color( 237, 239, 163 ) },
  99. { COLOR_PHONEME_EMPHASIS_BORDER, 1, Color( 200, 200, 200 ) },
  100. { COLOR_PHONEME_EMPHASIS_LINECOLOR, 1, Color( 0, 0, 255 ) },
  101. { COLOR_PHONEME_EMPHASIS_DOTCOLOR, 1, Color( 0, 0, 255 ) },
  102. { COLOR_PHONEME_EMPHASIS_DOTCOLOR_SELECTED, 1, Color( 240, 80, 20 ) },
  103. { COLOR_PHONEME_EMPHASIS_TEXT, 1, Color( 0, 0, 0 ) },
  104. { COLOR_PHONEME_EMPHASIS_MIDLINE, 1, Color( 100, 150, 200 ) },
  105. };
  106. struct Extractor
  107. {
  108. PE_APITYPE apitype;
  109. CSysModule *module;
  110. IPhonemeExtractor *extractor;
  111. };
  112. CUtlVector< Extractor > g_Extractors;
  113. bool DoesExtractorExistFor( PE_APITYPE type )
  114. {
  115. for ( int i=0; i < g_Extractors.Count(); i++ )
  116. {
  117. if ( g_Extractors[i].apitype == type )
  118. return true;
  119. }
  120. return false;
  121. }
  122. //-----------------------------------------------------------------------------
  123. // Purpose: Implements the RIFF i/o interface on stdio
  124. //-----------------------------------------------------------------------------
  125. class StdIOReadBinary : public IFileReadBinary
  126. {
  127. public:
  128. FileHandle_t open( const char *pFileName )
  129. {
  130. return filesystem->Open( pFileName, "rb" );
  131. }
  132. int read( void *pOutput, int size, FileHandle_t file )
  133. {
  134. if ( !file )
  135. return 0;
  136. return filesystem->Read( pOutput, size, file );
  137. }
  138. void seek( FileHandle_t file, int pos )
  139. {
  140. if ( !file )
  141. return;
  142. filesystem->Seek( file, pos, FILESYSTEM_SEEK_HEAD );
  143. }
  144. unsigned int tell( FileHandle_t file )
  145. {
  146. if ( !file )
  147. return 0;
  148. return filesystem->Tell( file );
  149. }
  150. unsigned int size( FileHandle_t file )
  151. {
  152. if ( !file )
  153. return 0;
  154. return filesystem->Size( file );
  155. }
  156. void close( FileHandle_t file )
  157. {
  158. if ( !file )
  159. return;
  160. filesystem->Close( file );
  161. }
  162. };
  163. class StdIOWriteBinary : public IFileWriteBinary
  164. {
  165. public:
  166. FileHandle_t create( const char *pFileName )
  167. {
  168. MakeFileWriteable( pFileName );
  169. return filesystem->Open( pFileName, "wb" );
  170. }
  171. int write( void *pData, int size, FileHandle_t file )
  172. {
  173. return filesystem->Write( pData, size, file );
  174. }
  175. void close( FileHandle_t file )
  176. {
  177. filesystem->Close( file );
  178. }
  179. void seek( FileHandle_t file, int pos )
  180. {
  181. filesystem->Seek( file, pos, FILESYSTEM_SEEK_HEAD );
  182. }
  183. unsigned int tell( FileHandle_t file )
  184. {
  185. return filesystem->Tell( file );
  186. }
  187. };
  188. // Interface objects
  189. static StdIOWriteBinary io_out;
  190. static StdIOReadBinary io_in;
  191. class CPhonemeModeTab : public CTabWindow
  192. {
  193. public:
  194. typedef CTabWindow BaseClass;
  195. CPhonemeModeTab( mxWindow *parent, int x, int y, int w, int h, int id = 0, int style = 0 ) :
  196. CTabWindow( parent, x, y, w, h, id, style )
  197. {
  198. SetInverted( true );
  199. }
  200. virtual void ShowRightClickMenu( int mx, int my )
  201. {
  202. // Nothing
  203. }
  204. void Init( void )
  205. {
  206. add( "Phonemes" );
  207. add( "Emphasis" );
  208. select( 0 );
  209. }
  210. };
  211. PhonemeEditor * g_pPhonemeEditor = 0;
  212. //-----------------------------------------------------------------------------
  213. // Purpose:
  214. // Input : *parent -
  215. //-----------------------------------------------------------------------------
  216. PhonemeEditor::PhonemeEditor( mxWindow *parent ) :
  217. IFacePoserToolWindow( "PhonemeEditor", "Phoneme Editor" ),
  218. mxWindow( parent, 0, 0, 0, 0 )
  219. {
  220. SetAutoProcess( false );
  221. m_flPlaybackRate = 1.0f;
  222. m_flScrub = 0.0f;
  223. m_flScrubTarget = 0.0f;
  224. m_CurrentMode = MODE_PHONEMES;
  225. Emphasis_Init();
  226. SetupPhonemeEditorColors();
  227. m_bRedoPending = false;
  228. m_nUndoLevel = 0;
  229. m_bWordsActive = false;
  230. m_pWaveFile = NULL;
  231. m_pMixer = NULL;
  232. m_pEvent = NULL;
  233. m_nClickX = 0;
  234. m_WorkFile.m_bDirty = false;
  235. m_WorkFile.m_szWaveFile[ 0 ] = 0;
  236. m_WorkFile.m_szWorkingFile[ 0 ] = 0;
  237. m_WorkFile.m_szBasePath[ 0 ] = 0;
  238. m_nTickHeight = 20;
  239. m_flPixelsPerSecond = 500.0f;
  240. m_nTimeZoom = 100;
  241. m_nTimeZoomStep = TIME_ZOOM_STEP;
  242. m_pHorzScrollBar = new mxScrollbar( this, 0, 0, 18, 100, IDC_PHONEME_SCROLL, mxScrollbar::Horizontal );
  243. m_hPrevCursor = 0;
  244. m_nStartX = 0;
  245. m_nStartY = 0;
  246. m_nLastX = 0;
  247. m_nLastY = 0;
  248. m_nDragType = DRAGTYPE_NONE;
  249. SetClickedPhoneme( -1, -1 );
  250. m_nSelection[ 0 ] = m_nSelection[ 1 ] = 0;
  251. m_bSelectionActive = false;
  252. m_nSelectedPhonemeCount = 0;
  253. m_nSelectedWordCount = 0;
  254. m_btnSave = new mxButton( this, 0, 0, 16, 16, "Save (Ctrl+S)", IDC_SAVE_LINGUISTIC );
  255. m_btnRedoPhonemeExtraction = new mxButton( this, 38, 14, 80, 16, "Re-extract (Ctrl+R)", IDC_REDO_PHONEMEEXTRACTION );
  256. m_btnLoad = new mxButton( this, 0, 0, 0, 0, "Load (Ctrl+O)", IDC_LOADWAVEFILE );
  257. m_btnPlay = new mxButton( this, 0, 0, 16, 16, "Play (Spacebar)", IDC_PLAYBUTTON );
  258. m_pPlaybackRate = new mxSlider( this, 0, 0, 16, 16, IDC_PLAYBACKRATE );
  259. m_pPlaybackRate->setRange( 0.0, 2.0, 40 );
  260. m_pPlaybackRate->setValue( m_flPlaybackRate );
  261. m_pModeTab = new CPhonemeModeTab( this, 0, 0, 500, 20, IDC_MODE_TAB );
  262. m_pModeTab->Init();
  263. m_nLastExtractionResult = SR_RESULT_NORESULT;
  264. ClearDragLimit();
  265. SetSuffix( " - Normal" );
  266. m_flScrubberTimeOffset = 0.0f;
  267. LoadPhonemeConverters();
  268. }
  269. //-----------------------------------------------------------------------------
  270. // Purpose:
  271. //-----------------------------------------------------------------------------
  272. void PhonemeEditor::OnDelete()
  273. {
  274. if ( m_pWaveFile )
  275. {
  276. char fn[ 512 ];
  277. Q_snprintf( fn, sizeof( fn ), "%s%s", m_WorkFile.m_szBasePath, m_WorkFile.m_szWorkingFile );
  278. filesystem->RemoveFile( fn, "GAME" );
  279. }
  280. delete m_pWaveFile;
  281. m_pWaveFile = NULL;
  282. m_Tags.Reset();
  283. m_TagsExt.Reset();
  284. UnloadPhonemeConverters();
  285. }
  286. //-----------------------------------------------------------------------------
  287. // Purpose:
  288. // Output : Returns true on success, false on failure.
  289. //-----------------------------------------------------------------------------
  290. bool PhonemeEditor::CanClose()
  291. {
  292. if ( !GetDirty() )
  293. return true;
  294. int retval = mxMessageBox( this, va( "Save current changes to %s", m_WorkFile.m_szWaveFile ),
  295. "Phoneme Editor", MX_MB_QUESTION | MX_MB_YESNOCANCEL );
  296. // Cancel
  297. if ( retval == 2 )
  298. {
  299. return false;
  300. }
  301. // Yes
  302. if ( retval == 0 )
  303. {
  304. CommitChanges();
  305. }
  306. return true;
  307. }
  308. //-----------------------------------------------------------------------------
  309. // Purpose:
  310. //-----------------------------------------------------------------------------
  311. PhonemeEditor::~PhonemeEditor( void )
  312. {
  313. }
  314. void PhonemeEditor::SetupPhonemeEditorColors( void )
  315. {
  316. int i;
  317. for ( i = 0; i < NUM_COLORS; i++ )
  318. {
  319. PhonemeEditorColor *p = &g_PEColors[ i ];
  320. Assert( p->color_number == i );
  321. if ( p->mode_number == -1 )
  322. {
  323. p->gray_color = p->root_color;
  324. }
  325. else
  326. {
  327. Color bgColor = g_PEColors[ COLOR_PHONEME_BACKGROUND ].root_color;
  328. int bgr, bgg, bgb;
  329. bgr = bgColor.r();
  330. bgg = bgColor.g();
  331. bgb = bgColor.b();
  332. int r, g, b;
  333. r = p->root_color.r();
  334. g = p->root_color.g();
  335. b = p->root_color.b();
  336. int avg = ( r + g + b ) / 3;
  337. int bgavg = ( bgr + bgg + bgb ) / 3;
  338. // Bias toward bg color
  339. avg += ( bgavg - avg ) / 2.5;
  340. p->gray_color = Color( avg, avg, avg );
  341. }
  342. }
  343. }
  344. Color PhonemeEditor::PEColor( int colornum )
  345. {
  346. Color clr = Color( 0, 0, 0 );
  347. if ( colornum < 0 || colornum >= NUM_COLORS )
  348. {
  349. Assert( 0 );
  350. return clr;
  351. }
  352. PhonemeEditorColor *p = &g_PEColors[ colornum ];
  353. if ( p->mode_number == -1 )
  354. {
  355. return p->root_color;
  356. }
  357. int modenum = (int)GetMode();
  358. if ( p->mode_number == modenum )
  359. {
  360. return p->root_color;
  361. }
  362. return p->gray_color;
  363. }
  364. void PhonemeEditor::EditWord( CWordTag *pWord, bool positionDialog /*= false*/ )
  365. {
  366. if ( !pWord )
  367. {
  368. Con_Printf( "PhonemeEditor::EditWord: pWord == NULL\n" );
  369. return;
  370. }
  371. CInputParams params;
  372. memset( &params, 0, sizeof( params ) );
  373. strcpy( params.m_szDialogTitle, "Edit Word" );
  374. strcpy( params.m_szPrompt, "Current Word:" );
  375. strcpy( params.m_szInputText, pWord->GetWord() );
  376. params.m_nLeft = -1;
  377. params.m_nTop = -1;
  378. params.m_bPositionDialog = positionDialog;
  379. if ( params.m_bPositionDialog )
  380. {
  381. RECT rcWord;
  382. GetWordRect( pWord, rcWord );
  383. // Convert to screen coords
  384. POINT pt;
  385. pt.x = rcWord.left;
  386. pt.y = rcWord.top;
  387. ClientToScreen( (HWND)getHandle(), &pt );
  388. params.m_nLeft = pt.x;
  389. params.m_nTop = pt.y;
  390. }
  391. int iret = InputProperties( &params );
  392. SetFocus( (HWND)getHandle() );
  393. if ( !iret )
  394. {
  395. return;
  396. }
  397. // Validate parameters
  398. if ( CSentence::CountWords( params.m_szInputText ) != 1 )
  399. {
  400. Con_ErrorPrintf( "Edit word: %s has multiple words in it!!!\n", params.m_szInputText );
  401. return;
  402. }
  403. SetFocus( (HWND)getHandle() );
  404. SetDirty( true );
  405. PushUndo();
  406. // Set the word and clear out the phonemes
  407. // ->m_nPhonemeCode = TextToPhoneme( params.m_szName );
  408. pWord->SetWord( params.m_szInputText );
  409. PushRedo();
  410. redraw();
  411. }
  412. //-----------------------------------------------------------------------------
  413. // Purpose:
  414. // Input : *pPhoneme -
  415. // positionDialog -
  416. //-----------------------------------------------------------------------------
  417. void PhonemeEditor::EditPhoneme( CPhonemeTag *pPhoneme, bool positionDialog /*= false*/ )
  418. {
  419. if ( !pPhoneme )
  420. {
  421. Con_Printf( "PhonemeEditor::EditPhoneme: pPhoneme == NULL\n" );
  422. return;
  423. }
  424. CPhonemeParams params;
  425. memset( &params, 0, sizeof( params ) );
  426. strcpy( params.m_szDialogTitle, "Phoneme/Viseme Properties" );
  427. strcpy( params.m_szName, ConvertPhoneme( pPhoneme->GetPhonemeCode() ) );
  428. params.m_nLeft = -1;
  429. params.m_nTop = -1;
  430. params.m_bPositionDialog = positionDialog;
  431. if ( params.m_bPositionDialog )
  432. {
  433. RECT rcPhoneme;
  434. GetPhonemeRect( pPhoneme, rcPhoneme );
  435. // Convert to screen coords
  436. POINT pt;
  437. pt.x = rcPhoneme.left;
  438. pt.y = rcPhoneme.top;
  439. ClientToScreen( (HWND)getHandle(), &pt );
  440. params.m_nLeft = pt.x;
  441. params.m_nTop = pt.y;
  442. }
  443. int iret = PhonemeProperties( &params );
  444. SetFocus( (HWND)getHandle() );
  445. if ( !iret )
  446. {
  447. return;
  448. }
  449. SetDirty( true );
  450. PushUndo();
  451. pPhoneme->SetPhonemeCode( TextToPhoneme( params.m_szName ) );
  452. PushRedo();
  453. redraw();
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Purpose:
  457. //-----------------------------------------------------------------------------
  458. void PhonemeEditor::EditPhoneme( void )
  459. {
  460. if ( GetMode() != MODE_PHONEMES )
  461. return;
  462. CPhonemeTag *pPhoneme = GetClickedPhoneme();
  463. if ( !pPhoneme )
  464. return;
  465. EditPhoneme( pPhoneme, false );
  466. }
  467. //-----------------------------------------------------------------------------
  468. // Purpose:
  469. //-----------------------------------------------------------------------------
  470. void PhonemeEditor::EditWord( void )
  471. {
  472. if ( GetMode() != MODE_PHONEMES )
  473. return;
  474. CWordTag *pWord = GetClickedWord();
  475. if ( !pWord )
  476. return;
  477. EditWord( pWord, false );
  478. }
  479. //-----------------------------------------------------------------------------
  480. // Purpose:
  481. // Input : dragtype -
  482. // startx -
  483. // cursor -
  484. //-----------------------------------------------------------------------------
  485. void PhonemeEditor::StartDragging( int dragtype, int startx, int starty, HCURSOR cursor )
  486. {
  487. m_nDragType = dragtype;
  488. m_nStartX = startx;
  489. m_nLastX = startx;
  490. m_nStartY = starty;
  491. m_nLastY = starty;
  492. if ( m_hPrevCursor )
  493. {
  494. SetCursor( m_hPrevCursor );
  495. m_hPrevCursor = NULL;
  496. }
  497. m_hPrevCursor = SetCursor( cursor );
  498. m_FocusRects.Purge();
  499. RECT rc;
  500. GetWorkspaceRect( rc );
  501. RECT rcStart;
  502. rcStart.left = startx;
  503. rcStart.right = startx;
  504. bool addrect = true;
  505. switch ( dragtype )
  506. {
  507. default:
  508. case DRAGTYPE_SCRUBBER:
  509. {
  510. RECT rcScrub;
  511. GetScrubHandleRect( rcScrub, true );
  512. rcStart = rcScrub;
  513. rcStart.left = ( rcScrub.left + rcScrub.right ) / 2;
  514. rcStart.right = rcStart.left;
  515. rcStart.bottom = h2() - 18 - MODE_TAB_OFFSET;
  516. }
  517. break;
  518. case DRAGTYPE_EMPHASIS_SELECT:
  519. {
  520. RECT rcEmphasis;
  521. Emphasis_GetRect( rc, rcEmphasis );
  522. rcStart.top = starty;
  523. rcStart.bottom = starty;
  524. }
  525. break;
  526. case DRAGTYPE_EMPHASIS_MOVE:
  527. {
  528. SetDirty( true );
  529. PushUndo();
  530. Emphasis_MouseDrag( startx, starty );
  531. m_Tags.Resort();
  532. addrect = false;
  533. }
  534. break;
  535. case DRAGTYPE_SELECTSAMPLES:
  536. case DRAGTYPE_MOVESELECTIONSTART:
  537. case DRAGTYPE_MOVESELECTIONEND:
  538. rcStart.top = rc.top;
  539. rcStart.bottom = rc.bottom;
  540. break;
  541. case DRAGTYPE_MOVESELECTION:
  542. {
  543. rcStart.top = rc.top;
  544. rcStart.bottom = rc.bottom;
  545. // Compute left/right pixels for selection
  546. rcStart.left = GetPixelForSample( m_nSelection[ 0 ] );
  547. rcStart.right = GetPixelForSample( m_nSelection[ 1 ] );
  548. }
  549. break;
  550. case DRAGTYPE_PHONEME:
  551. {
  552. GetPhonemeTrayTopBottom( rcStart );
  553. m_bWordsActive = false;
  554. }
  555. break;
  556. case DRAGTYPE_WORD:
  557. {
  558. GetWordTrayTopBottom( rcStart );
  559. m_bWordsActive = true;
  560. }
  561. break;
  562. case DRAGTYPE_MOVEWORD:
  563. {
  564. TraverseWords( &PhonemeEditor::ITER_AddFocusRectSelectedWords, 0.0f );
  565. addrect = false;
  566. m_bWordsActive = true;
  567. }
  568. break;
  569. case DRAGTYPE_MOVEPHONEME:
  570. {
  571. TraversePhonemes( &PhonemeEditor::ITER_AddFocusRectSelectedPhonemes, 0.0f );
  572. addrect = false;
  573. m_bWordsActive = false;
  574. }
  575. break;
  576. case DRAGTYPE_EVENTTAG_MOVE:
  577. {
  578. rcStart.top = TAG_TOP;
  579. rcStart.bottom = TAG_BOTTOM;
  580. rcStart.left -= 10;
  581. rcStart.right += 10;
  582. }
  583. break;
  584. }
  585. if ( addrect )
  586. {
  587. AddFocusRect( rcStart );
  588. }
  589. DrawFocusRect( "start" );
  590. SetDragLimit( m_nDragType );
  591. }
  592. //-----------------------------------------------------------------------------
  593. // Purpose:
  594. // Input : *event -
  595. // Output : int
  596. //-----------------------------------------------------------------------------
  597. int PhonemeEditor::handleEvent( mxEvent *event )
  598. {
  599. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  600. int iret = 0;
  601. if ( HandleToolEvent( event ) )
  602. {
  603. return iret;
  604. }
  605. switch ( event->event )
  606. {
  607. case mxEvent::Action:
  608. {
  609. iret = 1;
  610. switch ( event->action )
  611. {
  612. case IDC_EXPORT_SENTENCE:
  613. {
  614. OnExport();
  615. }
  616. break;
  617. case IDC_IMPORT_SENTENCE:
  618. {
  619. OnImport();
  620. }
  621. break;
  622. case IDC_PLAYBACKRATE:
  623. {
  624. m_flPlaybackRate = m_pPlaybackRate->getValue();
  625. redraw();
  626. }
  627. break;
  628. case IDC_MODE_TAB:
  629. {
  630. // The mode changed, so reset stuff here
  631. EditorMode newMode = (EditorMode)m_pModeTab->getSelectedIndex();
  632. bool needpaint = ( m_CurrentMode != newMode );
  633. m_CurrentMode = newMode;
  634. if ( needpaint )
  635. {
  636. switch ( GetMode() )
  637. {
  638. default:
  639. case MODE_PHONEMES:
  640. SetSuffix( " - Normal" );
  641. break;
  642. case MODE_EMPHASIS:
  643. SetSuffix( " - Emphasis Track" );
  644. break;
  645. }
  646. OnModeChanged();
  647. redraw();
  648. }
  649. }
  650. break;
  651. case IDC_EMPHASIS_DELETE:
  652. Emphasis_Delete();
  653. break;
  654. case IDC_EMPHASIS_DESELECT:
  655. Emphasis_DeselectAll();
  656. break;
  657. case IDC_EMPHASIS_SELECTALL:
  658. Emphasis_SelectAll();
  659. break;
  660. case IDC_API_SAPI:
  661. OnSAPI();
  662. break;
  663. case IDC_API_LIPSINC:
  664. OnLipSinc();
  665. break;
  666. case IDC_PLAYBUTTON:
  667. Play();
  668. break;
  669. case IDC_UNDO:
  670. Undo();
  671. break;
  672. case IDC_REDO:
  673. Redo();
  674. break;
  675. case IDC_CLEARUNDO:
  676. ClearUndo();
  677. break;
  678. case IDC_ADDTAG:
  679. AddTag();
  680. break;
  681. case IDC_DELETETAG:
  682. DeleteTag();
  683. break;
  684. case IDC_COMMITEXTRACTED:
  685. CommitExtracted();
  686. SetFocus( (HWND)getHandle() );
  687. break;
  688. case IDC_CLEAREXTRACTED:
  689. ClearExtracted();
  690. break;
  691. case IDC_SEPARATEPHONEMES:
  692. SeparatePhonemes();
  693. break;
  694. case IDC_SNAPPHONEMES:
  695. SnapPhonemes();
  696. break;
  697. case IDC_SEPARATEWORDS:
  698. SeparateWords();
  699. break;
  700. case IDC_SNAPWORDS:
  701. SnapWords();
  702. break;
  703. case IDC_EDITWORDLIST:
  704. EditWordList();
  705. break;
  706. case IDC_EDIT_PHONEME:
  707. EditPhoneme();
  708. break;
  709. case IDC_EDIT_WORD:
  710. EditWord();
  711. break;
  712. case IDC_EDIT_INSERTPHONEMEBEFORE:
  713. EditInsertPhonemeBefore();
  714. break;
  715. case IDC_EDIT_INSERTPHONEMEAFTER:
  716. EditInsertPhonemeAfter();
  717. break;
  718. case IDC_EDIT_INSERTWORDBEFORE:
  719. EditInsertWordBefore();
  720. break;
  721. case IDC_EDIT_INSERTWORDAFTER:
  722. EditInsertWordAfter();
  723. break;
  724. case IDC_EDIT_DELETEPHONEME:
  725. EditDeletePhoneme();
  726. break;
  727. case IDC_EDIT_DELETEWORD:
  728. EditDeleteWord();
  729. break;
  730. case IDC_EDIT_INSERTFIRSTPHONEMEOFWORD:
  731. EditInsertFirstPhonemeOfWord();
  732. break;
  733. case IDC_PHONEME_PLAY_ORIG:
  734. {
  735. StopPlayback();
  736. if ( m_pWaveFile )
  737. {
  738. // Make sure phonemes are loaded
  739. FacePoser_EnsurePhonemesLoaded();
  740. sound->PlaySound( m_pWaveFile, VOL_NORM, &m_pMixer );
  741. }
  742. }
  743. break;
  744. case IDC_PHONEME_SCROLL:
  745. if (event->modifiers == SB_THUMBTRACK)
  746. {
  747. MoveTimeSliderToPos( event->height );
  748. }
  749. else if ( event->modifiers == SB_PAGEUP )
  750. {
  751. int offset = m_pHorzScrollBar->getValue();
  752. offset -= 10;
  753. offset = max( offset, m_pHorzScrollBar->getMinValue() );
  754. MoveTimeSliderToPos( offset );
  755. }
  756. else if ( event->modifiers == SB_PAGEDOWN )
  757. {
  758. int offset = m_pHorzScrollBar->getValue();
  759. offset += 10;
  760. offset = min( offset, m_pHorzScrollBar->getMaxValue() );
  761. MoveTimeSliderToPos( offset );
  762. }
  763. break;
  764. case IDC_REDO_PHONEMEEXTRACTION:
  765. if ( m_Tags.m_Words.Count() <= 0 )
  766. {
  767. // This calls redo LISET if some words are actually entered
  768. EditWordList();
  769. }
  770. else
  771. {
  772. RedoPhonemeExtraction();
  773. }
  774. SetFocus( (HWND)getHandle() );
  775. break;
  776. case IDC_REDO_PHONEMEEXTRACTION_SELECTION:
  777. {
  778. RedoPhonemeExtractionSelected();
  779. }
  780. SetFocus( (HWND)getHandle() );
  781. break;
  782. case IDC_DESELECT:
  783. Deselect();
  784. redraw();
  785. break;
  786. case IDC_PLAY_EDITED:
  787. PlayEditedWave( false );
  788. SetFocus( (HWND)getHandle() );
  789. break;
  790. case IDC_PLAY_EDITED_SELECTION:
  791. PlayEditedWave( true );
  792. SetFocus( (HWND)getHandle() );
  793. break;
  794. case IDC_SAVE_LINGUISTIC:
  795. CommitChanges();
  796. SetFocus( (HWND)getHandle() );
  797. break;
  798. case IDC_LOADWAVEFILE:
  799. LoadWaveFile();
  800. SetFocus( (HWND)getHandle() );
  801. break;
  802. case IDC_CANCELPLAYBACK:
  803. StopPlayback();
  804. SetFocus( (HWND)getHandle() );
  805. break;
  806. case IDC_SELECT_WORDSRIGHT:
  807. SelectWords( true );
  808. break;
  809. case IDC_SELECT_WORDSLEFT:
  810. SelectWords( false );
  811. break;
  812. case IDC_SELECT_PHONEMESRIGHT:
  813. SelectPhonemes( true );
  814. break;
  815. case IDC_SELECT_PHONEMESLEFT:
  816. SelectPhonemes( false );
  817. break;
  818. case IDC_DESELECT_PHONEMESANDWORDS:
  819. DeselectPhonemes();
  820. DeselectWords();
  821. redraw();
  822. break;
  823. case IDC_CLEANUP:
  824. CleanupWordsAndPhonemes( true );
  825. redraw();
  826. break;
  827. case IDC_REALIGNPHONEMES:
  828. RealignPhonemesToWords( true );
  829. redraw();
  830. break;
  831. case IDC_REALIGNWORDS:
  832. RealignWordsToPhonemes( true );
  833. redraw();
  834. break;
  835. case IDC_TOGGLE_VOICEDUCK:
  836. OnToggleVoiceDuck();
  837. break;
  838. }
  839. if ( iret == 1 )
  840. {
  841. SetActiveTool( this );
  842. SetFocus( (HWND)getHandle() );
  843. }
  844. }
  845. break;
  846. case mxEvent::MouseWheeled:
  847. {
  848. // Zoom time in / out
  849. if ( event->height > 0 )
  850. {
  851. m_nTimeZoom = min( m_nTimeZoom + m_nTimeZoomStep, MAX_TIME_ZOOM );
  852. }
  853. else
  854. {
  855. m_nTimeZoom = max( m_nTimeZoom - m_nTimeZoomStep, m_nTimeZoomStep );
  856. }
  857. RepositionHSlider();
  858. iret = 1;
  859. }
  860. break;
  861. case mxEvent::Size:
  862. {
  863. int bw = 100;
  864. int x = 5;
  865. int by = h2() - 18 - MODE_TAB_OFFSET;
  866. m_pModeTab->setBounds( 0, h2() - MODE_TAB_OFFSET, w2(), MODE_TAB_OFFSET );
  867. m_btnRedoPhonemeExtraction->setBounds( x, by, bw, 16 );
  868. x += bw;
  869. m_btnSave->setBounds( x, by, bw, 16 );
  870. x += bw;
  871. m_btnLoad->setBounds( x, by, bw, 16 );
  872. x += bw;
  873. m_btnPlay->setBounds( x, by, bw, 16 );
  874. x += bw;
  875. m_pPlaybackRate->setBounds( x, by, 100, 16 );
  876. RepositionHSlider();
  877. iret = 1;
  878. }
  879. break;
  880. case mxEvent::MouseDown:
  881. {
  882. iret = 1;
  883. CPhonemeTag *pt;
  884. CWordTag *wt;
  885. pt = GetPhonemeTagUnderMouse( (short)event->x, (short)event->y );
  886. wt = GetWordTagUnderMouse( (short)event->x, (short)event->y );
  887. bool ctrldown = ( event->modifiers & mxEvent::KeyCtrl ) ? true : false;
  888. bool shiftdown = ( event->modifiers & mxEvent::KeyShift ) ? true : false;
  889. if ( event->buttons & mxEvent::MouseRightButton )
  890. {
  891. RECT rc;
  892. GetWorkspaceRect( rc );
  893. if ( IsMouseOverWordRow( (short)event->y ) )
  894. {
  895. ShowWordMenu( wt, (short)event->x, (short)event->y );
  896. }
  897. else if ( IsMouseOverPhonemeRow( (short)event->y ) )
  898. {
  899. ShowPhonemeMenu( pt, (short)event->x, (short)event->y );
  900. }
  901. else if ( IsMouseOverTagRow( (short)event->y ) )
  902. {
  903. ShowTagMenu( (short)event->x, (short)event->y );
  904. }
  905. else if ( IsMouseOverScrubArea( event ) )
  906. {
  907. float t = GetTimeForPixel( (short)event->x );
  908. ClampTimeToSelectionInterval( t );
  909. SetScrubTime( t );
  910. SetScrubTargetTime( t );
  911. redraw();
  912. }
  913. else
  914. {
  915. ShowContextMenu( (short)event->x, (short)event->y );
  916. }
  917. return iret;
  918. }
  919. if ( m_nDragType == DRAGTYPE_NONE )
  920. {
  921. CountSelected();
  922. int type = IsMouseOverBoundary( event );
  923. if ( IsMouseOverScrubArea( event ) )
  924. {
  925. if ( IsMouseOverScrubHandle( event ) )
  926. {
  927. StartDragging( DRAGTYPE_SCRUBBER,
  928. (short)event->x,
  929. (short)event->y,
  930. LoadCursor( NULL, IDC_SIZEWE ) );
  931. float t = GetTimeForPixel( (short)event->x );
  932. m_flScrubberTimeOffset = m_flScrub - t;
  933. float maxoffset = 0.5f * (float)SCRUBBER_HANDLE_WIDTH / GetPixelsPerSecond();
  934. m_flScrubberTimeOffset = clamp( m_flScrubberTimeOffset, -maxoffset, maxoffset );
  935. t += m_flScrubberTimeOffset;
  936. ClampTimeToSelectionInterval( t );
  937. SetScrubTime( t );
  938. SetScrubTargetTime( t );
  939. DrawScrubHandle();
  940. iret = true;
  941. }
  942. else
  943. {
  944. float t = GetTimeForPixel( (short)event->x );
  945. ClampTimeToSelectionInterval( t );
  946. SetScrubTargetTime( t );
  947. iret = true;
  948. }
  949. return iret;
  950. }
  951. else if ( GetMode() == MODE_EMPHASIS )
  952. {
  953. CEmphasisSample *sample = Emphasis_GetSampleUnderMouse( event );
  954. if ( sample )
  955. {
  956. if ( shiftdown )
  957. {
  958. sample->selected = !sample->selected;
  959. redraw();
  960. }
  961. else if ( sample->selected )
  962. {
  963. StartDragging( DRAGTYPE_EMPHASIS_MOVE, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEALL ) );
  964. }
  965. else
  966. {
  967. if ( !shiftdown )
  968. {
  969. Emphasis_DeselectAll();
  970. redraw();
  971. }
  972. StartDragging( DRAGTYPE_EMPHASIS_SELECT, (short)event->x, (short)event->y, NULL );
  973. }
  974. return true;
  975. }
  976. else if ( ctrldown )
  977. {
  978. // Add a sample point
  979. float t = GetTimeForPixel( (short)event->x );
  980. RECT rcWork;
  981. GetWorkspaceRect( rcWork );
  982. RECT rcEmphasis;
  983. Emphasis_GetRect( rcWork, rcEmphasis );
  984. int eh = rcEmphasis.bottom - rcEmphasis.top;
  985. int dy = (short)event->y - rcEmphasis.top;
  986. CEmphasisSample sample;
  987. sample.time = t;
  988. Assert( eh >= 0 );
  989. sample.value = (float)( dy ) / ( float ) eh;
  990. sample.value = 1.0f - clamp( sample.value, 0.0f, 1.0f );
  991. sample.selected = false;
  992. Emphasis_AddSample( sample );
  993. redraw();
  994. return true;
  995. }
  996. else
  997. {
  998. if ( !shiftdown )
  999. {
  1000. Emphasis_DeselectAll();
  1001. redraw();
  1002. }
  1003. StartDragging( DRAGTYPE_EMPHASIS_SELECT, (short)event->x, (short)event->y, NULL );
  1004. return true;
  1005. }
  1006. }
  1007. else
  1008. {
  1009. if ( type == BOUNDARY_PHONEME && m_nSelectedPhonemeCount <= 1 )
  1010. {
  1011. StartDragging( DRAGTYPE_PHONEME, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEWE ) );
  1012. return true;
  1013. }
  1014. else if ( type == BOUNDARY_WORD && m_nSelectedWordCount <= 1 )
  1015. {
  1016. StartDragging( DRAGTYPE_WORD, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEWE ) );
  1017. return true;
  1018. }
  1019. else if ( IsMouseOverSamples( (short)event->x, (short)event->y ) )
  1020. {
  1021. if ( !m_bSelectionActive )
  1022. {
  1023. StartDragging( DRAGTYPE_SELECTSAMPLES, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEWE ) );
  1024. }
  1025. else
  1026. {
  1027. // Either move, move edge if ctrl key is held, or deselect
  1028. if ( IsMouseOverSelection( (short)event->x, (short)event->y ) )
  1029. {
  1030. if ( IsMouseOverSelectionStartEdge( event ) )
  1031. {
  1032. StartDragging( DRAGTYPE_MOVESELECTIONSTART, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEWE ) );
  1033. }
  1034. else if ( IsMouseOverSelectionEndEdge( event ) )
  1035. {
  1036. StartDragging( DRAGTYPE_MOVESELECTIONEND, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEWE ) );
  1037. }
  1038. else
  1039. {
  1040. if ( shiftdown )
  1041. {
  1042. StartDragging( DRAGTYPE_MOVESELECTION, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEALL ) );
  1043. }
  1044. }
  1045. }
  1046. else
  1047. {
  1048. Deselect();
  1049. redraw();
  1050. return iret;
  1051. }
  1052. }
  1053. return true;
  1054. }
  1055. }
  1056. if ( IsMouseOverTag( (short)event->x, (short)event->y ) )
  1057. {
  1058. StartDragging( DRAGTYPE_EVENTTAG_MOVE, (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEALL ) );
  1059. return true;
  1060. }
  1061. else
  1062. {
  1063. if ( pt )
  1064. {
  1065. // Can only move when holding down shift key
  1066. if ( shiftdown )
  1067. {
  1068. pt->m_bSelected = true;
  1069. StartDragging( DRAGTYPE_MOVEPHONEME,
  1070. (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEALL ) );
  1071. }
  1072. else
  1073. {
  1074. // toggle the selection
  1075. pt->m_bSelected = !pt->m_bSelected;
  1076. }
  1077. m_bWordsActive = false;
  1078. redraw();
  1079. return true;
  1080. }
  1081. else if ( wt )
  1082. {
  1083. // Can only move when holding down shift key
  1084. if ( shiftdown )
  1085. {
  1086. wt->m_bSelected = true;
  1087. StartDragging( DRAGTYPE_MOVEWORD,
  1088. (short)event->x, (short)event->y, LoadCursor( NULL, IDC_SIZEALL ) );
  1089. }
  1090. else
  1091. {
  1092. // toggle the selection
  1093. wt->m_bSelected = !wt->m_bSelected;
  1094. }
  1095. m_bWordsActive = true;
  1096. redraw();
  1097. return true;
  1098. }
  1099. else if ( type == BOUNDARY_NONE )
  1100. {
  1101. DeselectPhonemes();
  1102. DeselectWords();
  1103. redraw();
  1104. return true;
  1105. }
  1106. }
  1107. }
  1108. }
  1109. break;
  1110. case mxEvent::MouseMove:
  1111. case mxEvent::MouseDrag:
  1112. {
  1113. OnMouseMove( event );
  1114. iret = 1;
  1115. }
  1116. break;
  1117. case mxEvent::MouseUp:
  1118. {
  1119. if ( m_nDragType != DRAGTYPE_NONE )
  1120. {
  1121. int mx = (short)event->x;
  1122. LimitDrag( mx );
  1123. event->x = (short)mx;
  1124. DrawFocusRect( "finish" );
  1125. if ( m_hPrevCursor )
  1126. {
  1127. SetCursor( m_hPrevCursor );
  1128. m_hPrevCursor = 0;
  1129. }
  1130. switch ( m_nDragType )
  1131. {
  1132. case DRAGTYPE_WORD:
  1133. FinishWordMove( m_nStartX, (short)event->x );
  1134. break;
  1135. case DRAGTYPE_PHONEME:
  1136. FinishPhonemeMove( m_nStartX, (short)event->x );
  1137. break;
  1138. case DRAGTYPE_SELECTSAMPLES:
  1139. FinishSelect( m_nStartX, (short)event->x );
  1140. break;
  1141. case DRAGTYPE_MOVESELECTION:
  1142. FinishMoveSelection( m_nStartX, (short)event->x );
  1143. break;
  1144. case DRAGTYPE_MOVESELECTIONSTART:
  1145. FinishMoveSelectionStart( m_nStartX, (short)event->x );
  1146. break;
  1147. case DRAGTYPE_MOVESELECTIONEND:
  1148. FinishMoveSelectionEnd( m_nStartX, (short)event->x );
  1149. break;
  1150. case DRAGTYPE_MOVEWORD:
  1151. FinishWordDrag( m_nStartX, (short)event->x );
  1152. break;
  1153. case DRAGTYPE_MOVEPHONEME:
  1154. FinishPhonemeDrag( m_nStartX, (short)event->x );
  1155. break;
  1156. case DRAGTYPE_EVENTTAG_MOVE:
  1157. FinishEventTagDrag( m_nStartX, (short)event->x );
  1158. break;
  1159. case DRAGTYPE_EMPHASIS_MOVE:
  1160. {
  1161. Emphasis_MouseDrag( (short)event->x, (short)event->y );
  1162. m_Tags.Resort();
  1163. PushRedo();
  1164. redraw();
  1165. }
  1166. break;
  1167. case DRAGTYPE_EMPHASIS_SELECT:
  1168. {
  1169. Emphasis_SelectPoints();
  1170. redraw();
  1171. }
  1172. break;
  1173. case DRAGTYPE_SCRUBBER:
  1174. {
  1175. float t = GetTimeForPixel( (short)event->x );
  1176. t += m_flScrubberTimeOffset;
  1177. m_flScrubberTimeOffset = 0.0f;
  1178. ClampTimeToSelectionInterval( t );
  1179. SetScrubTime( t );
  1180. SetScrubTargetTime( t );
  1181. sound->Flush();
  1182. DrawScrubHandle();
  1183. }
  1184. break;
  1185. default:
  1186. break;
  1187. }
  1188. m_nDragType = DRAGTYPE_NONE;
  1189. }
  1190. iret = 1;
  1191. }
  1192. break;
  1193. case mxEvent::KeyUp:
  1194. {
  1195. bool shiftDown = GetAsyncKeyState( VK_SHIFT ) ? true : false;
  1196. bool ctrlDown = GetAsyncKeyState( VK_CONTROL ) ? true : false;
  1197. switch( event->key )
  1198. {
  1199. case VK_TAB:
  1200. {
  1201. int direction = shiftDown ? -1 : 1;
  1202. SelectNextWord( direction );
  1203. }
  1204. break;
  1205. case VK_NEXT:
  1206. case VK_PRIOR:
  1207. {
  1208. m_bWordsActive = event->key == VK_PRIOR ? true : false;
  1209. redraw();
  1210. }
  1211. break;
  1212. case VK_UP:
  1213. case VK_RETURN:
  1214. if ( m_bWordsActive )
  1215. {
  1216. if ( event->key == VK_UP ||
  1217. ctrlDown )
  1218. {
  1219. CountSelected();
  1220. if ( m_nSelectedWordCount == 1 )
  1221. {
  1222. // Find the selected one
  1223. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  1224. {
  1225. CWordTag *word = m_Tags.m_Words[ i ];
  1226. if ( !word || !word->m_bSelected )
  1227. continue;
  1228. EditWord( word, true );
  1229. }
  1230. }
  1231. }
  1232. }
  1233. else
  1234. {
  1235. if ( event->key == VK_UP ||
  1236. ctrlDown )
  1237. {
  1238. CountSelected();
  1239. if ( m_nSelectedPhonemeCount == 1 )
  1240. {
  1241. // Find the selected one
  1242. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  1243. {
  1244. CWordTag *word = m_Tags.m_Words[ i ];
  1245. if ( !word )
  1246. continue;
  1247. for ( int j = 0; j < word->m_Phonemes.Count(); j++ )
  1248. {
  1249. CPhonemeTag *phoneme = word->m_Phonemes[ j ];
  1250. if ( !phoneme )
  1251. continue;
  1252. if ( !phoneme->m_bSelected )
  1253. continue;
  1254. EditPhoneme( phoneme, true );
  1255. }
  1256. }
  1257. }
  1258. }
  1259. }
  1260. break;
  1261. case VK_DELETE:
  1262. if ( GetMode() == MODE_EMPHASIS )
  1263. {
  1264. Emphasis_Delete();
  1265. }
  1266. else
  1267. {
  1268. if ( m_bWordsActive )
  1269. {
  1270. EditDeleteWord();
  1271. }
  1272. else
  1273. {
  1274. EditDeletePhoneme();
  1275. }
  1276. }
  1277. break;
  1278. case VK_INSERT:
  1279. if ( m_bWordsActive )
  1280. {
  1281. if ( shiftDown )
  1282. {
  1283. EditInsertWordBefore();
  1284. }
  1285. else
  1286. {
  1287. EditInsertWordAfter();
  1288. }
  1289. }
  1290. else
  1291. {
  1292. if ( shiftDown )
  1293. {
  1294. EditInsertPhonemeBefore();
  1295. }
  1296. else
  1297. {
  1298. EditInsertPhonemeAfter();
  1299. }
  1300. }
  1301. break;
  1302. case VK_SPACE:
  1303. if ( m_pWaveFile && sound->IsSoundPlaying( m_pMixer ) )
  1304. {
  1305. Con_Printf( "Stopping playback\n" );
  1306. m_btnPlay->setLabel( "Play (Spacebar)" );
  1307. StopPlayback();
  1308. }
  1309. else
  1310. {
  1311. Con_Printf( "Playing .wav\n" );
  1312. m_btnPlay->setLabel( "Stop[ (Spacebar)" );
  1313. PlayEditedWave( m_bSelectionActive );
  1314. }
  1315. break;
  1316. case VK_SHIFT:
  1317. case VK_CONTROL:
  1318. {
  1319. // Force mouse move
  1320. POINT pt;
  1321. GetCursorPos( &pt );
  1322. SetCursorPos( pt.x, pt.y );
  1323. return 0;
  1324. }
  1325. break;
  1326. case VK_ESCAPE:
  1327. {
  1328. // If playing sound, stop it, otherwise, deselect all
  1329. if ( !StopPlayback() )
  1330. {
  1331. Deselect();
  1332. DeselectPhonemes();
  1333. DeselectWords();
  1334. Emphasis_DeselectAll();
  1335. redraw();
  1336. }
  1337. }
  1338. break;
  1339. case 'O':
  1340. {
  1341. if ( ctrlDown )
  1342. {
  1343. LoadWaveFile();
  1344. }
  1345. }
  1346. break;
  1347. case 'S':
  1348. {
  1349. if ( ctrlDown )
  1350. {
  1351. CommitChanges();
  1352. }
  1353. }
  1354. break;
  1355. case 'T':
  1356. {
  1357. if ( ctrlDown )
  1358. {
  1359. // Edit sentence text
  1360. EditWordList();
  1361. }
  1362. }
  1363. break;
  1364. case 'G':
  1365. {
  1366. if ( ctrlDown )
  1367. {
  1368. // Commit extraction
  1369. CommitExtracted();
  1370. }
  1371. }
  1372. break;
  1373. case 'R':
  1374. {
  1375. if ( ctrlDown )
  1376. {
  1377. RedoPhonemeExtraction();
  1378. }
  1379. }
  1380. break;
  1381. default:
  1382. break;
  1383. }
  1384. SetFocus( (HWND)getHandle() );
  1385. iret = 1;
  1386. }
  1387. break;
  1388. case mxEvent::KeyDown:
  1389. {
  1390. switch ( event->key )
  1391. {
  1392. case 'Z':
  1393. if ( GetAsyncKeyState( VK_CONTROL ) )
  1394. {
  1395. Undo();
  1396. }
  1397. break;
  1398. case 'Y':
  1399. if ( GetAsyncKeyState( VK_CONTROL ) )
  1400. {
  1401. Redo();
  1402. }
  1403. break;
  1404. case VK_RIGHT:
  1405. case VK_LEFT:
  1406. {
  1407. int direction = event->key == VK_LEFT ? -1 : 1;
  1408. if ( !m_bWordsActive )
  1409. {
  1410. if ( GetAsyncKeyState( VK_CONTROL ) )
  1411. {
  1412. ExtendSelectedPhonemeEndTime( direction );
  1413. }
  1414. else if ( GetAsyncKeyState( VK_SHIFT ) )
  1415. {
  1416. ShiftSelectedPhoneme( direction );
  1417. }
  1418. else
  1419. {
  1420. SelectNextPhoneme( direction );
  1421. }
  1422. }
  1423. else
  1424. {
  1425. if ( GetAsyncKeyState( VK_CONTROL ) )
  1426. {
  1427. ExtendSelectedWordEndTime( direction );
  1428. }
  1429. else if ( GetAsyncKeyState( VK_SHIFT ) )
  1430. {
  1431. ShiftSelectedWord( direction );
  1432. }
  1433. else
  1434. {
  1435. SelectNextWord( direction );
  1436. }
  1437. }
  1438. }
  1439. break;
  1440. case VK_RETURN:
  1441. {
  1442. }
  1443. break;
  1444. case VK_SHIFT:
  1445. case VK_CONTROL:
  1446. {
  1447. // Force mouse move
  1448. POINT pt;
  1449. GetCursorPos( &pt );
  1450. //SetCursorPos( pt.x -1, pt.y );
  1451. SetCursorPos( pt.x, pt.y );
  1452. return 0;
  1453. }
  1454. break;
  1455. default:
  1456. break;
  1457. }
  1458. iret = 1;
  1459. }
  1460. break;
  1461. }
  1462. return iret;
  1463. }
  1464. void PhonemeEditor::DrawWords( CChoreoWidgetDrawHelper& drawHelper, RECT& rcWorkSpace, CSentence& sentence, int type, bool showactive /* = true */ )
  1465. {
  1466. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  1467. float endtime = w2() / GetPixelsPerSecond() + starttime;
  1468. int ypos = rcWorkSpace.top + m_nTickHeight + 2;
  1469. if ( type == 1 )
  1470. {
  1471. ypos += m_nTickHeight + 5;
  1472. }
  1473. const char *fontName = "Arial";
  1474. bool drawselected;
  1475. for ( int pass = 0; pass < 2 ; pass++ )
  1476. {
  1477. drawselected = pass == 0 ? false : true;
  1478. for (int k = 0; k < sentence.m_Words.Count(); k++)
  1479. {
  1480. CWordTag *word = sentence.m_Words[ k ];
  1481. if ( !word )
  1482. continue;
  1483. if ( word->m_bSelected != drawselected )
  1484. continue;
  1485. bool hasselectedphonemes = false;
  1486. for ( int p = 0; p < word->m_Phonemes.Count() && !hasselectedphonemes; p++ )
  1487. {
  1488. CPhonemeTag *t = word->m_Phonemes[ p ];
  1489. if ( t->m_bSelected )
  1490. {
  1491. hasselectedphonemes = true;
  1492. }
  1493. }
  1494. float t1 = word->m_flStartTime;
  1495. float t2 = word->m_flEndTime;
  1496. // Tag it
  1497. float frac = ( t1 - starttime ) / ( endtime - starttime );
  1498. int xpos = ( int )( frac * rcWorkSpace.right );
  1499. if ( frac <= 0.0 )
  1500. xpos = 0;
  1501. // Draw duration
  1502. float frac2 = ( t2 - starttime ) / ( endtime - starttime );
  1503. if ( frac2 < 0.0 )
  1504. continue;
  1505. int xpos2 = ( int )( frac2 * rcWorkSpace.right );
  1506. // Draw line and vertical ticks
  1507. RECT rcWord;
  1508. rcWord.left = xpos;
  1509. rcWord.right = xpos2;
  1510. rcWord.top = ypos - m_nTickHeight + 1;
  1511. rcWord.bottom = ypos;
  1512. drawHelper.DrawFilledRect(
  1513. PEColor( word->m_bSelected ? COLOR_PHONEME_TAG_SELECTED : COLOR_PHONEME_TAG_FILLER_NORMAL ),
  1514. rcWord );
  1515. Color border = PEColor( word->m_bSelected ? COLOR_PHONEME_TAG_BORDER_SELECTED : COLOR_PHONEME_TAG_BORDER );
  1516. if ( showactive && m_bWordsActive )
  1517. {
  1518. drawHelper.DrawFilledRect( PEColor( COLOR_PHONEME_ACTIVE_BORDER ), xpos, ypos - m_nTickHeight, xpos2, ypos - m_nTickHeight + 4 );
  1519. }
  1520. drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos, ypos, xpos2, ypos );
  1521. drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos, ypos, xpos, ypos - m_nTickHeight );
  1522. drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos2, ypos, xpos2, ypos - m_nTickHeight );
  1523. drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos, ypos - m_nTickHeight, xpos2, ypos - m_nTickHeight );
  1524. if ( hasselectedphonemes )
  1525. {
  1526. drawHelper.DrawFilledRect( PEColor( COLOR_PHONEME_SELECTED_BORDER ), xpos, ypos - 3, xpos2, ypos );
  1527. }
  1528. //if ( frac >= 0.0 && frac <= 1.0 )
  1529. {
  1530. int fontsize = 9;
  1531. RECT rcText;
  1532. rcText.left = xpos;
  1533. rcText.right = xpos + 500;
  1534. rcText.top = ypos - m_nTickHeight + 4;
  1535. rcText.bottom = rcText.top + fontsize + 2;
  1536. int length = drawHelper.CalcTextWidth( fontName, fontsize, FW_NORMAL, "%s", word->GetWord() );
  1537. rcText.right = max( xpos2 - 2, rcText.left + length + 1 );
  1538. int w = rcText.right - rcText.left;
  1539. if ( w > length )
  1540. {
  1541. rcText.left += ( w - length ) / 2;
  1542. }
  1543. drawHelper.DrawColoredText(
  1544. fontName,
  1545. fontsize,
  1546. FW_NORMAL,
  1547. PEColor( word->m_bSelected ? COLOR_PHONEME_TAG_TEXT_SELECTED : COLOR_PHONEME_TAG_TEXT ),
  1548. rcText,
  1549. "%s", word->GetWord() );
  1550. }
  1551. }
  1552. }
  1553. }
  1554. void PhonemeEditor::DrawPhonemes( CChoreoWidgetDrawHelper& drawHelper, RECT& rcWorkSpace, CSentence& sentence, int type, bool showactive /* = true */ )
  1555. {
  1556. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  1557. float endtime = w2() / GetPixelsPerSecond() + starttime;
  1558. int ypos = rcWorkSpace.bottom - m_nTickHeight - 2;
  1559. if ( type == 1 )
  1560. {
  1561. ypos -= ( m_nTickHeight + 5 );
  1562. }
  1563. const char *fontName = "Arial";
  1564. bool drawselected;
  1565. for ( int pass = 0; pass < 2 ; pass++ )
  1566. {
  1567. drawselected = pass == 0 ? false : true;
  1568. for ( int i = 0; i < sentence.m_Words.Count(); i++ )
  1569. {
  1570. CWordTag *w = sentence.m_Words[ i ];
  1571. if ( !w )
  1572. continue;
  1573. if ( w->m_bSelected != drawselected )
  1574. continue;
  1575. for ( int k = 0; k < w->m_Phonemes.Count(); k++ )
  1576. {
  1577. CPhonemeTag *pPhoneme = w->m_Phonemes[ k ];
  1578. float t1 = pPhoneme->GetStartTime();
  1579. float t2 = pPhoneme->GetEndTime();
  1580. // Tag it
  1581. float frac = ( t1 - starttime ) / ( endtime - starttime );
  1582. int xpos = ( int )( frac * rcWorkSpace.right );
  1583. if ( frac <= 0.0 )
  1584. {
  1585. xpos = 0;
  1586. }
  1587. // Draw duration
  1588. float frac2 = ( t2 - starttime ) / ( endtime - starttime );
  1589. if ( frac2 < 0.0 )
  1590. {
  1591. continue;
  1592. }
  1593. int xpos2 = ( int )( frac2 * rcWorkSpace.right );
  1594. RECT rcFrame;
  1595. rcFrame.left = xpos;
  1596. rcFrame.right = xpos2;
  1597. rcFrame.top = ypos - m_nTickHeight + 1;
  1598. rcFrame.bottom = ypos;
  1599. drawHelper.DrawFilledRect(
  1600. PEColor( pPhoneme->m_bSelected ? COLOR_PHONEME_TAG_SELECTED : COLOR_PHONEME_TAG_FILLER_NORMAL ),
  1601. rcFrame );
  1602. Color border = PEColor( pPhoneme->m_bSelected ? COLOR_PHONEME_TAG_BORDER_SELECTED : COLOR_PHONEME_TAG_BORDER );
  1603. if ( showactive && !m_bWordsActive )
  1604. {
  1605. drawHelper.DrawFilledRect( PEColor( COLOR_PHONEME_ACTIVE_BORDER ), xpos, ypos - 3, xpos2, ypos );
  1606. }
  1607. drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos, ypos - m_nTickHeight, xpos2, ypos - m_nTickHeight );
  1608. drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos, ypos, xpos, ypos - m_nTickHeight );
  1609. drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos2, ypos, xpos2, ypos - m_nTickHeight );
  1610. drawHelper.DrawColoredLine( border, PS_SOLID, 1, xpos, ypos, xpos2, ypos );
  1611. if ( w->m_bSelected )
  1612. {
  1613. drawHelper.DrawFilledRect( PEColor( COLOR_PHONEME_SELECTED_BORDER ), xpos, ypos - m_nTickHeight + 1, xpos2, ypos - m_nTickHeight + 4 );
  1614. }
  1615. //if ( frac >= 0.0 && frac <= 1.0 )
  1616. {
  1617. int fontsize = 9;
  1618. RECT rcText;
  1619. rcText.left = xpos;
  1620. rcText.right = xpos + 500;
  1621. rcText.top = ypos - m_nTickHeight + 4;
  1622. rcText.bottom = rcText.top + fontsize + 2;
  1623. int length = drawHelper.CalcTextWidth( fontName, fontsize, FW_NORMAL, "%s", ConvertPhoneme( pPhoneme->GetPhonemeCode() ) );
  1624. rcText.right = max( xpos2 - 2, rcText.left + length + 1 );
  1625. int w = rcText.right - rcText.left;
  1626. if ( w > length )
  1627. {
  1628. rcText.left += ( w - length ) / 2;
  1629. }
  1630. drawHelper.DrawColoredText(
  1631. fontName,
  1632. fontsize,
  1633. FW_NORMAL,
  1634. PEColor( pPhoneme->m_bSelected ? COLOR_PHONEME_TAG_TEXT_SELECTED : COLOR_PHONEME_TAG_TEXT ),
  1635. rcText,
  1636. "%s", ConvertPhoneme( pPhoneme->GetPhonemeCode() ) );
  1637. }
  1638. }
  1639. }
  1640. }
  1641. }
  1642. //-----------------------------------------------------------------------------
  1643. // Purpose:
  1644. // Input : drawHelper -
  1645. // rc -
  1646. //-----------------------------------------------------------------------------
  1647. void PhonemeEditor::DrawRelativeTags( CChoreoWidgetDrawHelper& drawHelper, RECT& rc )
  1648. {
  1649. if ( !m_pEvent || !m_pWaveFile )
  1650. return;
  1651. drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, PEColor( COLOR_PHONEME_TIMING_TAG ), rc, "Timing Tags:" );
  1652. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  1653. float endtime = w2() / GetPixelsPerSecond() + starttime;
  1654. for ( int i = 0; i < m_pEvent->GetNumRelativeTags(); i++ )
  1655. {
  1656. CEventRelativeTag *tag = m_pEvent->GetRelativeTag( i );
  1657. if ( !tag )
  1658. continue;
  1659. //
  1660. float tagtime = tag->GetPercentage() * m_pWaveFile->GetRunningLength();
  1661. if ( tagtime < starttime || tagtime > endtime )
  1662. continue;
  1663. float frac = ( tagtime - starttime ) / ( endtime - starttime );
  1664. int left = rc.left + (int)( frac * ( float )( rc.right - rc.left ) + 0.5f );
  1665. RECT rcMark;
  1666. rcMark = rc;
  1667. rcMark.top = rc.bottom - 8;
  1668. rcMark.bottom = rc.bottom;
  1669. rcMark.left = left - 4;
  1670. rcMark.right = left + 4;
  1671. drawHelper.DrawTriangleMarker( rcMark, PEColor( COLOR_PHONEME_TIMING_TAG ) );
  1672. RECT rcText;
  1673. rcText = rc;
  1674. rcText.bottom = rc.bottom - 10;
  1675. rcText.top = rcText.bottom - 10;
  1676. int len = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, tag->GetName() );
  1677. rcText.left = left - len / 2;
  1678. rcText.right = rcText.left + len + 2;
  1679. drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, PEColor( COLOR_PHONEME_TIMING_TAG ), rcText, tag->GetName() );
  1680. }
  1681. }
  1682. //-----------------------------------------------------------------------------
  1683. // Purpose:
  1684. //-----------------------------------------------------------------------------
  1685. void PhonemeEditor::redraw( void )
  1686. {
  1687. if ( !ToolCanDraw() )
  1688. return;
  1689. CChoreoWidgetDrawHelper drawHelper( this );
  1690. HandleToolRedraw( drawHelper );
  1691. if ( !m_pWaveFile )
  1692. return;
  1693. HDC dc = drawHelper.GrabDC();
  1694. RECT rc;
  1695. GetWorkspaceRect( rc );
  1696. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  1697. float endtime = w2() / GetPixelsPerSecond() + starttime;
  1698. // Now draw the time legend
  1699. RECT rcLabel;
  1700. float granularity = 0.5f;
  1701. drawHelper.DrawColoredLine( PEColor( COLOR_PHONEME_TIMELINE ), PS_SOLID, 1, rc.left, rc.bottom - m_nTickHeight, rc.right, rc.bottom - m_nTickHeight );
  1702. if ( GetMode() != MODE_EMPHASIS )
  1703. {
  1704. Emphasis_Redraw( drawHelper, rc );
  1705. }
  1706. sound->RenderWavToDC(
  1707. dc,
  1708. rc,
  1709. PEColor( COLOR_PHONEME_WAVDATA ),
  1710. starttime,
  1711. endtime,
  1712. m_pWaveFile,
  1713. m_bSelectionActive,
  1714. m_nSelection[ 0 ],
  1715. m_nSelection[ 1 ] );
  1716. float f = SnapTime( starttime, granularity );
  1717. while ( f <= endtime )
  1718. {
  1719. float frac = ( f - starttime ) / ( endtime - starttime );
  1720. if ( frac >= 0.0f && frac <= 1.0f )
  1721. {
  1722. drawHelper.DrawColoredLine( PEColor( COLOR_PHONEME_TIMELINE_MAJORTICK ), PS_SOLID, 1, (int)( frac * rc.right ), rc.top, (int)( frac * rc.right ), rc.bottom - m_nTickHeight );
  1723. rcLabel.left = (int)( frac * rc.right );
  1724. rcLabel.bottom = rc.bottom;
  1725. rcLabel.top = rcLabel.bottom - 10;
  1726. char sz[ 32 ];
  1727. sprintf( sz, "%.2f", f );
  1728. int textWidth = drawHelper.CalcTextWidth( "Arial", 9, FW_NORMAL, sz );
  1729. rcLabel.right = rcLabel.left + textWidth;
  1730. OffsetRect( &rcLabel, -textWidth / 2, 0 );
  1731. drawHelper.DrawColoredText( "Arial", 9, FW_NORMAL, PEColor( COLOR_PHONEME_TEXT ), rcLabel, sz );
  1732. }
  1733. f += granularity;
  1734. }
  1735. HBRUSH br = CreateSolidBrush( ColorToRGB( PEColor( COLOR_PHONEME_TEXT ) ) );
  1736. FrameRect( dc, &rc, br );
  1737. DeleteObject( br );
  1738. RECT rcTags = rc;
  1739. rcTags.top = TAG_TOP;
  1740. rcTags.bottom = TAG_BOTTOM;
  1741. DrawRelativeTags( drawHelper, rcTags );
  1742. int fontsize = 9;
  1743. RECT rcText = rc;
  1744. rcText.top = rcText.bottom + 5;
  1745. rcText.left += 5;
  1746. rcText.bottom = rcText.top + fontsize + 1;
  1747. rcText.right -= 5;
  1748. int fontweight = FW_NORMAL;
  1749. const char *font = "Arial";
  1750. if ( m_nLastExtractionResult != SR_RESULT_NORESULT )
  1751. {
  1752. Color clr = PEColor( COLOR_PHONEME_EXTRACTION_RESULT_OTHER );
  1753. switch ( m_nLastExtractionResult )
  1754. {
  1755. case SR_RESULT_ERROR:
  1756. clr = PEColor( COLOR_PHONEME_EXTRACTION_RESULT_ERROR );
  1757. break;
  1758. case SR_RESULT_SUCCESS:
  1759. clr = PEColor( COLOR_PHONEME_EXTRACTION_RESULT_SUCCESS );
  1760. break;
  1761. case SR_RESULT_FAILED:
  1762. clr = PEColor( COLOR_PHONEME_EXTRACTION_RESULT_FAIL );
  1763. break;
  1764. default:
  1765. break;
  1766. }
  1767. drawHelper.DrawColoredText( font, fontsize, fontweight, clr, rcText,
  1768. "Last Extraction Result: %s", GetExtractionResultString( m_nLastExtractionResult ) );
  1769. OffsetRect( &rcText, 0, fontsize + 1 );
  1770. }
  1771. if ( m_pEvent && !Q_stristr( m_pEvent->GetParameters(), ".wav" ) )
  1772. {
  1773. drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
  1774. "Sound: '%s', file: %s, length %.2f seconds",
  1775. m_pEvent->GetParameters(),
  1776. m_WorkFile.m_szWaveFile,
  1777. m_pWaveFile->GetRunningLength() );
  1778. }
  1779. else
  1780. {
  1781. drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
  1782. "File: %s, length %.2f seconds", m_WorkFile.m_szWaveFile, m_pWaveFile->GetRunningLength() );
  1783. }
  1784. OffsetRect( &rcText, 0, fontsize + 1 );
  1785. drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
  1786. "Number of samples %i at %ikhz (%i bits/sample) %s", (int) (m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate() ), m_pWaveFile->SampleRate(), (m_pWaveFile->SampleSize()<<3), m_Tags.GetVoiceDuck() ? "duck other audio" : "no ducking" );
  1787. OffsetRect( &rcText, 0, fontsize + 1 );
  1788. drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
  1789. "[ %i ] Words [ %i ] Phonemes / Zoom %i %%", m_Tags.m_Words.Count(), m_Tags.CountPhonemes(), m_nTimeZoom );
  1790. if ( m_pEvent )
  1791. {
  1792. OffsetRect( &rcText, 0, fontsize + 1 );
  1793. drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
  1794. "Event %s", m_pEvent->GetName() );
  1795. }
  1796. OffsetRect( &rcText, 0, fontsize + 1 );
  1797. drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
  1798. "Using: %s", GetSpeechAPIName() );
  1799. char text[ 4096 ];
  1800. sprintf( text, "Sentence Text: %s", m_Tags.GetText() );
  1801. int halfwidth = ( rc.right - rc.left ) / 2;
  1802. rcText = rc;
  1803. rcText.left = halfwidth;
  1804. rcText.top = rcText.bottom + 5;
  1805. rcText.right = rcText.left + halfwidth * 0.6;
  1806. drawHelper.CalcTextRect( font, fontsize, fontweight, halfwidth, rcText, text );
  1807. drawHelper.DrawColoredTextMultiline( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText,
  1808. text );
  1809. CWordTag *cw = GetSelectedWord();
  1810. if ( cw )
  1811. {
  1812. char wordInfo[ 512 ];
  1813. sprintf( wordInfo, "Word: %s, start %.2f end %.2f, duration %.2f ms phonemes %i",
  1814. cw->GetWord(), cw->m_flStartTime, cw->m_flEndTime, 1000.0f * ( cw->m_flEndTime - cw->m_flStartTime ),
  1815. cw->m_Phonemes.Count() );
  1816. int length = drawHelper.CalcTextWidth( font, fontsize, fontweight, wordInfo );
  1817. OffsetRect( &rcText, 0, ( rcText.bottom - rcText.top ) + 2 );
  1818. rcText.left = rcText.right - length - 10;
  1819. rcText.bottom = rcText.top + fontsize + 1;
  1820. drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText, wordInfo );
  1821. }
  1822. CPhonemeTag *cp = GetSelectedPhoneme();
  1823. if ( cp )
  1824. {
  1825. char phonemeInfo[ 512 ];
  1826. sprintf( phonemeInfo, "Phoneme: %s, start %.2f end %.2f, duration %.2f ms",
  1827. ConvertPhoneme( cp->GetPhonemeCode() ), cp->GetStartTime(), cp->GetEndTime(), 1000.0f * ( cp->GetEndTime() - cp->GetStartTime() ) );
  1828. int length = drawHelper.CalcTextWidth( font, fontsize, fontweight, phonemeInfo );
  1829. OffsetRect( &rcText, 0, ( rcText.bottom - rcText.top ) + 2 );
  1830. rcText.left = rcText.right - length - 10;
  1831. rcText.bottom = rcText.top + fontsize + 1;
  1832. drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_TEXT ), rcText, phonemeInfo );
  1833. }
  1834. // Draw playback rate
  1835. {
  1836. char sz[ 48 ];
  1837. sprintf( sz, "Speed: %.2fx", m_flPlaybackRate );
  1838. int length = drawHelper.CalcTextWidth( font, fontsize, fontweight, sz);
  1839. rcText = rc;
  1840. rcText.top = rc.bottom + 60;
  1841. rcText.bottom = rcText.top + fontsize + 1;
  1842. rcText.left = m_pPlaybackRate->x() + m_pPlaybackRate->w() - x();
  1843. rcText.right = rcText.left + length + 2;
  1844. drawHelper.DrawColoredText( font, fontsize, fontweight,
  1845. PEColor( COLOR_PHONEME_TEXT ), rcText, sz );
  1846. }
  1847. if ( m_UndoStack.Count() > 0 )
  1848. {
  1849. int length = drawHelper.CalcTextWidth( font, fontsize, fontweight,
  1850. "Undo levels: %i/%i", m_nUndoLevel, m_UndoStack.Count() );
  1851. rcText = rc;
  1852. rcText.top = rc.bottom + 60;
  1853. rcText.bottom = rcText.top + fontsize + 1;
  1854. rcText.right -= 5;
  1855. rcText.left = rcText.right - length - 10;
  1856. drawHelper.DrawColoredText( font, fontsize, fontweight, PEColor( COLOR_PHONEME_EXTRACTION_RESULT_SUCCESS ), rcText,
  1857. "Undo levels: %i/%i", m_nUndoLevel, m_UndoStack.Count() );
  1858. }
  1859. float endfrac = ( m_pWaveFile->GetRunningLength() - starttime ) / ( endtime - starttime );
  1860. if ( endfrac >= 0.0f && endfrac <= 1.0f )
  1861. {
  1862. int endpos = ( int ) ( rc.right * endfrac );
  1863. drawHelper.DrawColoredLine( PEColor( COLOR_PHONEME_WAV_ENDPOINT ), PS_DOT, 2, endpos, rc.top, endpos, rc.bottom - m_nTickHeight );
  1864. }
  1865. DrawPhonemes( drawHelper, rc, m_Tags, 0 );
  1866. DrawPhonemes( drawHelper, rc, m_TagsExt, 1, false );
  1867. DrawWords( drawHelper, rc, m_Tags, 0 );
  1868. DrawWords( drawHelper, rc, m_TagsExt, 1, false );
  1869. if ( GetMode() == MODE_EMPHASIS )
  1870. {
  1871. Emphasis_Redraw( drawHelper, rc );
  1872. }
  1873. DrawScrubHandle( drawHelper );
  1874. }
  1875. #define MOTION_RANGE 3000
  1876. #define MOTION_MAXSTEP 500
  1877. //-----------------------------------------------------------------------------
  1878. // Purpose: Brown noise simulates brownian motion centered around 127.5 but we cap the walking
  1879. // to just a couple of units
  1880. // Input : *buffer -
  1881. // count -
  1882. // Output : static void
  1883. //-----------------------------------------------------------------------------
  1884. static void WriteBrownNoise( void *buffer, int count )
  1885. {
  1886. int currentValue = 127500;
  1887. int maxValue = currentValue + ( MOTION_RANGE / 2 );
  1888. int minValue = currentValue - ( MOTION_RANGE / 2 );
  1889. unsigned char *pos = ( unsigned char *)buffer;
  1890. while ( --count >= 0 )
  1891. {
  1892. currentValue += random->RandomInt( -MOTION_MAXSTEP, MOTION_MAXSTEP );
  1893. currentValue = min( maxValue, currentValue );
  1894. currentValue = max( minValue, currentValue );
  1895. // Downsample to 0-255 range
  1896. *pos++ = (unsigned char)( ( (float)currentValue / 1000.0f ) + 0.5f );
  1897. }
  1898. }
  1899. //-----------------------------------------------------------------------------
  1900. // Purpose: Replace with brownian noice parts of the wav file that we dont' want processed by the
  1901. // speech recognizer
  1902. // Input : store -
  1903. // *format -
  1904. // chunkname -
  1905. // *buffer -
  1906. // buffersize -
  1907. //-----------------------------------------------------------------------------
  1908. void PhonemeEditor::ResampleChunk( IterateOutputRIFF& store, void *format, int chunkname, char *buffer, int buffersize, int start_silence /*=0*/, int end_silence /*=0*/ )
  1909. {
  1910. WAVEFORMATEX *pFormat = ( WAVEFORMATEX * )format;
  1911. Assert( pFormat );
  1912. if ( pFormat->wFormatTag == WAVE_FORMAT_PCM )
  1913. {
  1914. int silience_time = start_silence + end_silence;
  1915. // Leave room for silence at start + end
  1916. int resamplesize = buffersize + silience_time * pFormat->nSamplesPerSec;
  1917. char *resamplebuffer = new char[ resamplesize + 4 ];
  1918. memset( resamplebuffer, (unsigned char)128, resamplesize + 4 );
  1919. int startpos = (int)( start_silence * pFormat->nSamplesPerSec );
  1920. if ( startpos > 0 )
  1921. {
  1922. WriteBrownNoise( resamplebuffer, startpos );
  1923. }
  1924. if ( startpos + buffersize < resamplesize )
  1925. {
  1926. WriteBrownNoise( &resamplebuffer[ startpos + buffersize ], resamplesize - ( startpos + buffersize ) );
  1927. }
  1928. memcpy( &resamplebuffer[ startpos ], buffer, buffersize );
  1929. store.ChunkWriteData( resamplebuffer, resamplesize );
  1930. return;
  1931. }
  1932. store.ChunkWriteData( buffer, buffersize );
  1933. }
  1934. //-----------------------------------------------------------------------------
  1935. // Purpose:
  1936. //-----------------------------------------------------------------------------
  1937. void PhonemeEditor::ReadLinguisticTags( void )
  1938. {
  1939. if ( !m_pWaveFile )
  1940. return;
  1941. CAudioSource *wave = sound->LoadSound( m_WorkFile.m_szWorkingFile );
  1942. if ( !wave )
  1943. return;
  1944. m_Tags.Reset();
  1945. CSentence *sentence = wave->GetSentence();
  1946. if ( sentence )
  1947. {
  1948. // Copy data from sentence to m_Tags
  1949. m_Tags.Reset();
  1950. m_Tags = *sentence;
  1951. }
  1952. delete wave;
  1953. }
  1954. //-----------------------------------------------------------------------------
  1955. // Purpose: Switch wave files
  1956. // Input : *wavefile -
  1957. // force -
  1958. //-----------------------------------------------------------------------------
  1959. void PhonemeEditor::SetCurrentWaveFile( const char *wavefile, bool force /*=false*/, CChoreoEvent *event /*=NULL*/ )
  1960. {
  1961. // No change?
  1962. if ( !force && !stricmp( m_WorkFile.m_szWaveFile, wavefile ) )
  1963. return;
  1964. StopPlayback();
  1965. if ( GetDirty() )
  1966. {
  1967. int retval = mxMessageBox( this, va( "Save current changes to %s", m_WorkFile.m_szWaveFile ),
  1968. "Phoneme Editor", MX_MB_QUESTION | MX_MB_YESNOCANCEL );
  1969. // Cancel
  1970. if ( retval == 2 )
  1971. return;
  1972. // Yes
  1973. if ( retval == 0 )
  1974. {
  1975. CommitChanges();
  1976. }
  1977. }
  1978. ClearExtracted();
  1979. m_Tags.Reset();
  1980. m_TagsExt.Reset();
  1981. Deselect();
  1982. if ( m_pWaveFile )
  1983. {
  1984. char fn[ 512 ];
  1985. Q_snprintf( fn, sizeof( fn ), "%s%s", m_WorkFile.m_szBasePath, m_WorkFile.m_szWorkingFile );
  1986. filesystem->RemoveFile( fn, "GAME" );
  1987. }
  1988. delete m_pWaveFile;
  1989. m_pWaveFile = NULL;
  1990. SetDirty( false );
  1991. // Set up event and scene
  1992. m_pEvent = event;
  1993. // Try an dload new sound
  1994. m_pWaveFile = sound->LoadSound( wavefile );
  1995. Q_strncpy( m_WorkFile.m_szWaveFile, wavefile, sizeof( m_WorkFile.m_szWaveFile ) );
  1996. char fullpath[ 512 ];
  1997. filesystem->RelativePathToFullPath( wavefile, "GAME", fullpath, sizeof( fullpath ) );
  1998. int len = Q_strlen( fullpath );
  1999. int charstocopy = len - Q_strlen( wavefile ) + 1;
  2000. m_WorkFile.m_szBasePath[ 0 ] = 0;
  2001. if ( charstocopy >= 0 )
  2002. {
  2003. Q_strncpy( m_WorkFile.m_szBasePath, fullpath, charstocopy );
  2004. m_WorkFile.m_szBasePath[ charstocopy ] = 0;
  2005. }
  2006. Q_StripExtension( wavefile, m_WorkFile.m_szWorkingFile, sizeof( m_WorkFile.m_szWorkingFile ) );
  2007. Q_strncat( m_WorkFile.m_szWorkingFile, "_work.wav", sizeof( m_WorkFile.m_szWorkingFile ), COPY_ALL_CHARACTERS );
  2008. Q_FixSlashes( m_WorkFile.m_szWaveFile );
  2009. Q_FixSlashes( m_WorkFile.m_szWorkingFile );
  2010. Q_FixSlashes( m_WorkFile.m_szBasePath );
  2011. if ( !m_pWaveFile )
  2012. {
  2013. Con_ErrorPrintf( "Couldn't set current .wav file to %s\n", m_WorkFile.m_szWaveFile );
  2014. return;
  2015. }
  2016. Con_Printf( "Current .wav file set to %s\n", m_WorkFile.m_szWaveFile );
  2017. g_pWaveBrowser->SetCurrent( m_WorkFile.m_szWaveFile );
  2018. // Copy over and overwrite file
  2019. FPCopyFile( m_WorkFile.m_szWaveFile, m_WorkFile.m_szWorkingFile, false );
  2020. // Make it writable
  2021. MakeFileWriteable( m_WorkFile.m_szWorkingFile );
  2022. ReadLinguisticTags();
  2023. Deselect();
  2024. RepositionHSlider();
  2025. }
  2026. //-----------------------------------------------------------------------------
  2027. // Purpose:
  2028. // Input : x -
  2029. //-----------------------------------------------------------------------------
  2030. void PhonemeEditor::MoveTimeSliderToPos( int x )
  2031. {
  2032. m_nLeftOffset = x;
  2033. m_pHorzScrollBar->setValue( m_nLeftOffset );
  2034. InvalidateRect( (HWND)m_pHorzScrollBar->getHandle(), NULL, TRUE );
  2035. redraw();
  2036. }
  2037. //-----------------------------------------------------------------------------
  2038. // Purpose:
  2039. // Output : int
  2040. //-----------------------------------------------------------------------------
  2041. int PhonemeEditor::ComputeHPixelsNeeded( void )
  2042. {
  2043. int pixels = 0;
  2044. if ( m_pWaveFile )
  2045. {
  2046. float maxtime = m_pWaveFile->GetRunningLength();
  2047. maxtime += 1.0f;
  2048. pixels = (int)( maxtime * GetPixelsPerSecond() );
  2049. }
  2050. return pixels;
  2051. }
  2052. //-----------------------------------------------------------------------------
  2053. // Purpose:
  2054. //-----------------------------------------------------------------------------
  2055. void PhonemeEditor::RepositionHSlider( void )
  2056. {
  2057. int pixelsneeded = ComputeHPixelsNeeded();
  2058. if ( pixelsneeded <= w2() )
  2059. {
  2060. m_pHorzScrollBar->setVisible( false );
  2061. }
  2062. else
  2063. {
  2064. m_pHorzScrollBar->setVisible( true );
  2065. }
  2066. m_pHorzScrollBar->setBounds( 0, GetCaptionHeight(), w2(), 12 );
  2067. m_pHorzScrollBar->setRange( 0, pixelsneeded );
  2068. m_pHorzScrollBar->setValue( 0 );
  2069. m_nLeftOffset = 0;
  2070. m_pHorzScrollBar->setPagesize( w2() );
  2071. redraw();
  2072. }
  2073. //-----------------------------------------------------------------------------
  2074. // Purpose:
  2075. // Output : float
  2076. //-----------------------------------------------------------------------------
  2077. float PhonemeEditor::GetPixelsPerSecond( void )
  2078. {
  2079. return m_flPixelsPerSecond * GetTimeZoomScale();
  2080. }
  2081. //-----------------------------------------------------------------------------
  2082. // Purpose:
  2083. // Output : float
  2084. //-----------------------------------------------------------------------------
  2085. float PhonemeEditor::GetTimeZoomScale( void )
  2086. {
  2087. return ( float )m_nTimeZoom / 100.0f;
  2088. }
  2089. //-----------------------------------------------------------------------------
  2090. // Purpose:
  2091. // Input : scale -
  2092. //-----------------------------------------------------------------------------
  2093. void PhonemeEditor::SetTimeZoomScale( int scale )
  2094. {
  2095. m_nTimeZoom = scale;
  2096. }
  2097. //-----------------------------------------------------------------------------
  2098. // Purpose:
  2099. // Input : dt -
  2100. //-----------------------------------------------------------------------------
  2101. void PhonemeEditor::Think( float dt )
  2102. {
  2103. if ( !m_pWaveFile )
  2104. return;
  2105. bool scrubbing = ( m_nDragType == DRAGTYPE_SCRUBBER ) ? true : false;
  2106. ScrubThink( dt, scrubbing );
  2107. if ( m_pMixer && !sound->IsSoundPlaying( m_pMixer ) )
  2108. {
  2109. m_pMixer = NULL;
  2110. }
  2111. }
  2112. //-----------------------------------------------------------------------------
  2113. // Purpose:
  2114. // Input : mx -
  2115. // my -
  2116. // Output : Returns true on success, false on failure.
  2117. //-----------------------------------------------------------------------------
  2118. int PhonemeEditor::IsMouseOverBoundary( mxEvent *event )
  2119. {
  2120. int mx, my;
  2121. mx = (short)event->x;
  2122. my = (short)event->y;
  2123. // Deterime if phoneme boundary is under the cursor
  2124. //
  2125. if ( !m_pWaveFile )
  2126. return BOUNDARY_NONE;
  2127. if ( !(event->modifiers & mxEvent::KeyCtrl ) )
  2128. {
  2129. return BOUNDARY_NONE;
  2130. }
  2131. RECT rc;
  2132. GetWorkspaceRect( rc );
  2133. if ( IsMouseOverPhonemeRow( my ) )
  2134. {
  2135. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  2136. float endtime = w2() / GetPixelsPerSecond() + starttime;
  2137. int mouse_tolerance = 3;
  2138. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  2139. {
  2140. CWordTag *word = m_Tags.m_Words[ i ];
  2141. for ( int k = 0; k < word->m_Phonemes.Count(); k++ )
  2142. {
  2143. CPhonemeTag *pPhoneme = word->m_Phonemes[ k ];
  2144. float t1 = pPhoneme->GetStartTime();
  2145. float t2 = pPhoneme->GetEndTime();
  2146. // Tag it
  2147. float frac1 = ( t1 - starttime ) / ( endtime - starttime );
  2148. float frac2 = ( t2 - starttime ) / ( endtime - starttime );
  2149. int xpos1 = ( int )( frac1 * w2() );
  2150. int xpos2 = ( int )( frac2 * w2() );
  2151. if ( abs( xpos1 - mx ) <= mouse_tolerance ||
  2152. abs( xpos2 - mx ) <= mouse_tolerance )
  2153. {
  2154. return BOUNDARY_PHONEME;
  2155. }
  2156. }
  2157. }
  2158. }
  2159. if ( IsMouseOverWordRow( my ) )
  2160. {
  2161. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  2162. float endtime = w2() / GetPixelsPerSecond() + starttime;
  2163. int mouse_tolerance = 3;
  2164. for ( int k = 0; k < m_Tags.m_Words.Count(); k++ )
  2165. {
  2166. CWordTag *word = m_Tags.m_Words[ k ];
  2167. float t1 = word->m_flStartTime;
  2168. float t2 = word->m_flEndTime;
  2169. // Tag it
  2170. float frac1 = ( t1 - starttime ) / ( endtime - starttime );
  2171. float frac2 = ( t2 - starttime ) / ( endtime - starttime );
  2172. int xpos1 = ( int )( frac1 * w2() );
  2173. int xpos2 = ( int )( frac2 * w2() );
  2174. if ( ( abs( xpos1 - mx ) <= mouse_tolerance ) ||
  2175. ( abs( xpos2 - mx ) <= mouse_tolerance ) )
  2176. {
  2177. return BOUNDARY_WORD;
  2178. }
  2179. }
  2180. }
  2181. return BOUNDARY_NONE;
  2182. }
  2183. //-----------------------------------------------------------------------------
  2184. // Purpose:
  2185. //-----------------------------------------------------------------------------
  2186. void PhonemeEditor::DrawFocusRect( char *reason )
  2187. {
  2188. HDC dc = GetDC( NULL );
  2189. for ( int i = 0; i < m_FocusRects.Count(); i++ )
  2190. {
  2191. RECT rc = m_FocusRects[ i ].m_rcFocus;
  2192. ::DrawFocusRect( dc, &rc );
  2193. }
  2194. ReleaseDC( NULL, dc );
  2195. }
  2196. //-----------------------------------------------------------------------------
  2197. // Purpose:
  2198. // Input : &rc -
  2199. //-----------------------------------------------------------------------------
  2200. void PhonemeEditor::GetWorkspaceRect( RECT &rc )
  2201. {
  2202. GetClientRect( (HWND)getHandle(), &rc );
  2203. rc.top += TAG_BOTTOM;
  2204. rc.bottom = rc.bottom - 75 - MODE_TAB_OFFSET;
  2205. InflateRect( &rc, -1, -1 );
  2206. }
  2207. //-----------------------------------------------------------------------------
  2208. // Purpose:
  2209. // Input : mx -
  2210. // my -
  2211. //-----------------------------------------------------------------------------
  2212. void PhonemeEditor::ShowWordMenu( CWordTag *word, int mx, int my )
  2213. {
  2214. CountSelected();
  2215. mxPopupMenu *pop = new mxPopupMenu();
  2216. Assert( pop );
  2217. pop->add( va( "Edit sentence text..." ), IDC_EDITWORDLIST );
  2218. if ( m_nSelectedWordCount > 0 && word )
  2219. {
  2220. pop->addSeparator();
  2221. pop->add( va( "Delete %s", m_nSelectedWordCount > 1 ? "words" : va( "'%s'", word->GetWord() ) ), IDC_EDIT_DELETEWORD );
  2222. if ( m_nSelectedWordCount == 1 )
  2223. {
  2224. int index = IndexOfWord( word );
  2225. bool valid = false;
  2226. if ( index != -1 )
  2227. {
  2228. SetClickedPhoneme( index, -1 );
  2229. valid = true;
  2230. }
  2231. if ( valid )
  2232. {
  2233. pop->add( va( "Edit word '%s'...", word->GetWord() ), IDC_EDIT_WORD );
  2234. float nextGap = GetTimeGapToNextWord( true, word );
  2235. float prevGap = GetTimeGapToNextWord( false, word );
  2236. if ( nextGap > MINIMUM_WORD_GAP ||
  2237. prevGap > MINIMUM_WORD_GAP )
  2238. {
  2239. pop->addSeparator();
  2240. if ( prevGap > MINIMUM_WORD_GAP )
  2241. {
  2242. pop->add( va( "Insert word before '%s'...", word->GetWord() ), IDC_EDIT_INSERTWORDBEFORE );
  2243. }
  2244. if ( nextGap > MINIMUM_WORD_GAP )
  2245. {
  2246. pop->add( va( "Insert word after '%s'...", word->GetWord() ), IDC_EDIT_INSERTWORDAFTER );
  2247. }
  2248. }
  2249. if ( word->m_Phonemes.Count() == 0 )
  2250. {
  2251. pop->addSeparator();
  2252. pop->add( va( "Add phoneme to '%s'...", word->GetWord() ), IDC_EDIT_INSERTFIRSTPHONEMEOFWORD );
  2253. }
  2254. pop->addSeparator();
  2255. pop->add( va( "Select all words after '%s'", word->GetWord() ), IDC_SELECT_WORDSRIGHT );
  2256. pop->add( va( "Select all words before '%s'", word->GetWord() ), IDC_SELECT_WORDSLEFT );
  2257. }
  2258. }
  2259. }
  2260. if ( AreSelectedWordsContiguous() && m_nSelectedWordCount > 1 )
  2261. {
  2262. pop->addSeparator();
  2263. pop->add( va( "Merge words" ), IDC_SNAPWORDS );
  2264. if ( m_nSelectedWordCount == 2 )
  2265. {
  2266. pop->add( va( "Separate words" ), IDC_SEPARATEWORDS );
  2267. }
  2268. }
  2269. if ( m_nSelectedWordCount > 0 )
  2270. {
  2271. pop->addSeparator();
  2272. pop->add( va( "Deselect all" ), IDC_DESELECT_PHONEMESANDWORDS );
  2273. }
  2274. if ( m_Tags.m_Words.Count() > 0 )
  2275. {
  2276. pop->addSeparator();
  2277. pop->add( va( "Cleanup words/phonemes" ), IDC_CLEANUP );
  2278. }
  2279. if ( m_Tags.m_Words.Count() > 0 )
  2280. {
  2281. pop->addSeparator();
  2282. pop->add( va( "Realign phonemes to words" ), IDC_REALIGNPHONEMES );
  2283. }
  2284. pop->popup( this, mx, my );
  2285. }
  2286. //-----------------------------------------------------------------------------
  2287. // Purpose:
  2288. // Input : mx -
  2289. // my -
  2290. //-----------------------------------------------------------------------------
  2291. void PhonemeEditor::ShowPhonemeMenu( CPhonemeTag *pho, int mx, int my )
  2292. {
  2293. CountSelected();
  2294. SetClickedPhoneme( -1, -1 );
  2295. if ( !pho )
  2296. return;
  2297. if ( m_Tags.CountPhonemes() == 0 )
  2298. {
  2299. Con_Printf( "No phonemes, try extracting from .wav first\n" );
  2300. return;
  2301. }
  2302. mxPopupMenu *pop = new mxPopupMenu();
  2303. bool valid = false;
  2304. CWordTag *tag = m_Tags.GetWordForPhoneme( pho );
  2305. if ( tag )
  2306. {
  2307. int wordNum = IndexOfWord( tag );
  2308. int pi = tag->IndexOfPhoneme( pho );
  2309. SetClickedPhoneme( wordNum, pi );
  2310. valid = true;
  2311. }
  2312. if ( valid )
  2313. {
  2314. if ( m_nSelectedPhonemeCount == 1 )
  2315. {
  2316. pop->add( va( "Edit '%s'...", ConvertPhoneme( pho->GetPhonemeCode() ) ), IDC_EDIT_PHONEME );
  2317. float nextGap = GetTimeGapToNextPhoneme( true, pho );
  2318. float prevGap = GetTimeGapToNextPhoneme( false, pho );
  2319. if ( nextGap > MINIMUM_PHONEME_GAP ||
  2320. prevGap > MINIMUM_PHONEME_GAP )
  2321. {
  2322. pop->addSeparator();
  2323. if ( prevGap > MINIMUM_PHONEME_GAP )
  2324. {
  2325. pop->add( va( "Insert phoneme before '%s'...", ConvertPhoneme( pho->GetPhonemeCode() ) ), IDC_EDIT_INSERTPHONEMEBEFORE );
  2326. }
  2327. if ( nextGap > MINIMUM_PHONEME_GAP )
  2328. {
  2329. pop->add( va( "Insert phoneme after '%s'...", ConvertPhoneme( pho->GetPhonemeCode() ) ), IDC_EDIT_INSERTPHONEMEAFTER );
  2330. }
  2331. }
  2332. pop->addSeparator();
  2333. pop->add( va( "Select all phonemes after '%s'", ConvertPhoneme( pho->GetPhonemeCode() ) ), IDC_SELECT_PHONEMESRIGHT );
  2334. pop->add( va( "Select all phonemes before '%s'",ConvertPhoneme( pho->GetPhonemeCode() ) ), IDC_SELECT_PHONEMESLEFT );
  2335. pop->addSeparator();
  2336. }
  2337. if ( AreSelectedPhonemesContiguous() && m_nSelectedPhonemeCount > 1 )
  2338. {
  2339. pop->add( va( "Merge phonemes" ), IDC_SNAPPHONEMES );
  2340. if ( m_nSelectedPhonemeCount == 2 )
  2341. {
  2342. pop->add( va( "Separate phonemes" ), IDC_SEPARATEPHONEMES );
  2343. }
  2344. pop->addSeparator();
  2345. }
  2346. if ( m_nSelectedPhonemeCount >= 1 )
  2347. {
  2348. pop->add( va( "Delete %s",
  2349. m_nSelectedPhonemeCount == 1 ? va( "'%s'", ConvertPhoneme( pho->GetPhonemeCode() ) ) : "phonemes" ), IDC_EDIT_DELETEPHONEME );
  2350. pop->addSeparator();
  2351. pop->add( va( "Deselect all" ), IDC_DESELECT_PHONEMESANDWORDS );
  2352. }
  2353. }
  2354. if ( m_Tags.m_Words.Count() > 0 )
  2355. {
  2356. pop->addSeparator();
  2357. pop->add( va( "Cleanup words/phonemes" ), IDC_CLEANUP );
  2358. }
  2359. if ( m_Tags.m_Words.Count() > 0 )
  2360. {
  2361. pop->addSeparator();
  2362. pop->add( va( "Realign words to phonemes" ), IDC_REALIGNWORDS );
  2363. }
  2364. pop->popup( this, mx, my );
  2365. }
  2366. //-----------------------------------------------------------------------------
  2367. // Purpose:
  2368. // Input : mx -
  2369. // Output : float
  2370. //-----------------------------------------------------------------------------
  2371. float PhonemeEditor::GetTimeForPixel( int mx )
  2372. {
  2373. RECT rc;
  2374. GetWorkspaceRect( rc );
  2375. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  2376. float time = (float)mx / GetPixelsPerSecond() + starttime;
  2377. return time;
  2378. }
  2379. //-----------------------------------------------------------------------------
  2380. // Purpose:
  2381. // Input : time -
  2382. // **pp1 -
  2383. // **pp2 -
  2384. // Output : Returns true on success, false on failure.
  2385. //-----------------------------------------------------------------------------
  2386. bool PhonemeEditor::FindSpanningPhonemes( float time, CPhonemeTag **pp1, CPhonemeTag **pp2 )
  2387. {
  2388. Assert( pp1 && pp2 );
  2389. *pp1 = NULL;
  2390. *pp2 = NULL;
  2391. // Three pixels
  2392. double time_epsilon = ( 1.0f / GetPixelsPerSecond() ) * 3;
  2393. CPhonemeTag *previous = NULL;
  2394. for ( int w = 0; w < m_Tags.m_Words.Count(); w++ )
  2395. {
  2396. CWordTag *word = m_Tags.m_Words[ w ];
  2397. for ( int i = 0; i < word->m_Phonemes.Count(); i++ )
  2398. {
  2399. CPhonemeTag *current = word->m_Phonemes[ i ];
  2400. double dt;
  2401. if ( !previous )
  2402. {
  2403. dt = fabs( current->GetStartTime() - time );
  2404. if ( dt < time_epsilon )
  2405. {
  2406. *pp2 = current;
  2407. return true;
  2408. }
  2409. }
  2410. else
  2411. {
  2412. int found = 0;
  2413. dt = fabs( previous->GetEndTime() - time );
  2414. if ( dt < time_epsilon )
  2415. {
  2416. *pp1 = previous;
  2417. found++;
  2418. }
  2419. dt = fabs( current->GetStartTime() - time );
  2420. if ( dt < time_epsilon )
  2421. {
  2422. *pp2 = current;
  2423. found++;
  2424. }
  2425. if ( found != 0 )
  2426. {
  2427. return true;
  2428. }
  2429. }
  2430. previous = current;
  2431. }
  2432. }
  2433. if ( m_Tags.m_Words.Count() > 0 )
  2434. {
  2435. // Check last word, but only if it has some phonemes
  2436. CWordTag *lastWord = m_Tags.m_Words[ m_Tags.m_Words.Count() - 1 ];
  2437. if ( lastWord &&
  2438. ( lastWord->m_Phonemes.Count() > 0 ) )
  2439. {
  2440. CPhonemeTag *last = lastWord->m_Phonemes[ lastWord->m_Phonemes.Count() - 1 ];
  2441. float dt;
  2442. dt = fabs( last->GetEndTime() - time );
  2443. if ( dt < time_epsilon )
  2444. {
  2445. *pp1 = last;
  2446. return true;
  2447. }
  2448. }
  2449. }
  2450. return false;
  2451. }
  2452. //-----------------------------------------------------------------------------
  2453. // Purpose:
  2454. // Input : time -
  2455. // **pp1 -
  2456. // **pp2 -
  2457. // Output : Returns true on success, false on failure.
  2458. //-----------------------------------------------------------------------------
  2459. bool PhonemeEditor::FindSpanningWords( float time, CWordTag **pp1, CWordTag **pp2 )
  2460. {
  2461. Assert( pp1 && pp2 );
  2462. *pp1 = NULL;
  2463. *pp2 = NULL;
  2464. // Three pixels
  2465. double time_epsilon = ( 1.0f / GetPixelsPerSecond() ) * 3;
  2466. CWordTag *previous = NULL;
  2467. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  2468. {
  2469. CWordTag *current = m_Tags.m_Words[ i ];
  2470. double dt;
  2471. if ( !previous )
  2472. {
  2473. dt = fabs( current->m_flStartTime - time );
  2474. if ( dt < time_epsilon )
  2475. {
  2476. *pp2 = current;
  2477. return true;
  2478. }
  2479. }
  2480. else
  2481. {
  2482. int found = 0;
  2483. dt = fabs( previous->m_flEndTime - time );
  2484. if ( dt < time_epsilon )
  2485. {
  2486. *pp1 = previous;
  2487. found++;
  2488. }
  2489. dt = fabs( current->m_flStartTime - time );
  2490. if ( dt < time_epsilon )
  2491. {
  2492. *pp2 = current;
  2493. found++;
  2494. }
  2495. if ( found != 0 )
  2496. {
  2497. return true;
  2498. }
  2499. }
  2500. previous = current;
  2501. }
  2502. if ( m_Tags.m_Words.Count() > 0 )
  2503. {
  2504. CWordTag *last = m_Tags.m_Words[ m_Tags.m_Words.Count() - 1 ];
  2505. float dt;
  2506. dt = fabs( last->m_flEndTime - time );
  2507. if ( dt < time_epsilon )
  2508. {
  2509. *pp1 = last;
  2510. return true;
  2511. }
  2512. }
  2513. return false;
  2514. }
  2515. int PhonemeEditor::FindWordForTime( float time )
  2516. {
  2517. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  2518. {
  2519. CWordTag *pCurrent = m_Tags.m_Words[ i ];
  2520. if ( time < pCurrent->m_flStartTime )
  2521. continue;
  2522. if ( time > pCurrent->m_flEndTime )
  2523. continue;
  2524. return i;
  2525. }
  2526. return -1;
  2527. }
  2528. void PhonemeEditor::FinishWordDrag( int startx, int endx )
  2529. {
  2530. float clicktime = GetTimeForPixel( startx );
  2531. float endtime = GetTimeForPixel( endx );
  2532. float dt = endtime - clicktime;
  2533. SetDirty( true );
  2534. PushUndo();
  2535. TraverseWords( &PhonemeEditor::ITER_MoveSelectedWords, dt );
  2536. RealignPhonemesToWords( false );
  2537. CleanupWordsAndPhonemes( false );
  2538. PushRedo();
  2539. redraw();
  2540. }
  2541. void PhonemeEditor::FinishWordMove( int startx, int endx )
  2542. {
  2543. float clicktime = GetTimeForPixel( startx );
  2544. float endtime = GetTimeForPixel( endx );
  2545. // Find the phonemes who have the closest start/endtime to the starting click time
  2546. CWordTag *current, *next;
  2547. if ( !FindSpanningWords( clicktime, &current, &next ) )
  2548. {
  2549. return;
  2550. }
  2551. SetDirty( true );
  2552. PushUndo();
  2553. if ( current && !next )
  2554. {
  2555. // cap movement
  2556. current->m_flEndTime += ( endtime - clicktime );
  2557. }
  2558. else if ( !current && next )
  2559. {
  2560. // cap movement
  2561. next->m_flStartTime += ( endtime - clicktime );
  2562. }
  2563. else
  2564. {
  2565. // cap movement
  2566. endtime = min( endtime, next->m_flEndTime - 1.0f / GetPixelsPerSecond() );
  2567. endtime = max( endtime, current->m_flStartTime + 1.0f / GetPixelsPerSecond() );
  2568. current->m_flEndTime = endtime;
  2569. next->m_flStartTime = endtime;
  2570. }
  2571. RealignPhonemesToWords( false );
  2572. CleanupWordsAndPhonemes( false );
  2573. PushRedo();
  2574. redraw();
  2575. }
  2576. CPhonemeTag *PhonemeEditor::FindPhonemeForTime( float time )
  2577. {
  2578. for ( int w = 0 ; w < m_Tags.m_Words.Count(); w++ )
  2579. {
  2580. CWordTag *word = m_Tags.m_Words[ w ];
  2581. for ( int i = 0; i < word->m_Phonemes.Count(); i++ )
  2582. {
  2583. CPhonemeTag *pCurrent = word->m_Phonemes[ i ];
  2584. if ( time < pCurrent->GetStartTime() )
  2585. continue;
  2586. if ( time > pCurrent->GetEndTime() )
  2587. continue;
  2588. return pCurrent;
  2589. }
  2590. }
  2591. return NULL;
  2592. }
  2593. //-----------------------------------------------------------------------------
  2594. // Purpose:
  2595. // Input : phoneme -
  2596. // startx -
  2597. // endx -
  2598. //-----------------------------------------------------------------------------
  2599. void PhonemeEditor::FinishPhonemeDrag( int startx, int endx )
  2600. {
  2601. float clicktime = GetTimeForPixel( startx );
  2602. float endtime = GetTimeForPixel( endx );
  2603. float dt = endtime - clicktime;
  2604. SetDirty( true );
  2605. PushUndo();
  2606. TraversePhonemes( &PhonemeEditor::ITER_MoveSelectedPhonemes, dt );
  2607. RealignWordsToPhonemes( false );
  2608. CleanupWordsAndPhonemes( false );
  2609. PushRedo();
  2610. redraw();
  2611. }
  2612. //-----------------------------------------------------------------------------
  2613. // Purpose:
  2614. // Input : phoneme -
  2615. // startx -
  2616. // endx -
  2617. //-----------------------------------------------------------------------------
  2618. void PhonemeEditor::FinishPhonemeMove( int startx, int endx )
  2619. {
  2620. float clicktime = GetTimeForPixel( startx );
  2621. float endtime = GetTimeForPixel( endx );
  2622. // Find the phonemes who have the closest start/endtime to the starting click time
  2623. CPhonemeTag *current, *next;
  2624. if ( !FindSpanningPhonemes( clicktime, &current, &next ) )
  2625. {
  2626. return;
  2627. }
  2628. SetDirty( true );
  2629. PushUndo();
  2630. if ( current && !next )
  2631. {
  2632. // cap movement
  2633. current->AddEndTime( endtime - clicktime );
  2634. }
  2635. else if ( !current && next )
  2636. {
  2637. // cap movement
  2638. next->AddStartTime( endtime - clicktime );
  2639. }
  2640. else
  2641. {
  2642. // cap movement
  2643. endtime = min( endtime, next->GetEndTime() - 1.0f / GetPixelsPerSecond() );
  2644. endtime = max( endtime, current->GetStartTime() + 1.0f / GetPixelsPerSecond() );
  2645. current->SetEndTime( endtime );
  2646. next->SetStartTime( endtime );
  2647. }
  2648. RealignWordsToPhonemes( false );
  2649. CleanupWordsAndPhonemes( false );
  2650. PushRedo();
  2651. redraw();
  2652. }
  2653. //-----------------------------------------------------------------------------
  2654. // Purpose:
  2655. // Input : dirty -
  2656. //-----------------------------------------------------------------------------
  2657. void PhonemeEditor::SetDirty( bool dirty, bool clearundo /*=true*/ )
  2658. {
  2659. m_WorkFile.m_bDirty = dirty;
  2660. if ( !dirty && clearundo )
  2661. {
  2662. WipeUndo();
  2663. redraw();
  2664. }
  2665. SetPrefix( dirty ? "* " : "" );
  2666. }
  2667. //-----------------------------------------------------------------------------
  2668. // Purpose:
  2669. // Output : Returns true on success, false on failure.
  2670. //-----------------------------------------------------------------------------
  2671. bool PhonemeEditor::GetDirty( void )
  2672. {
  2673. return m_WorkFile.m_bDirty;
  2674. }
  2675. //-----------------------------------------------------------------------------
  2676. // Purpose:
  2677. //-----------------------------------------------------------------------------
  2678. void PhonemeEditor::EditInsertPhonemeBefore( void )
  2679. {
  2680. if ( GetMode() != MODE_PHONEMES )
  2681. return;
  2682. CPhonemeTag *cp = GetSelectedPhoneme();
  2683. if ( !cp )
  2684. return;
  2685. float gap = GetTimeGapToNextPhoneme( false, cp );
  2686. if ( gap < MINIMUM_PHONEME_GAP )
  2687. {
  2688. Con_Printf( "Can't insert before, gap of %.2f ms is too small\n", 1000.0f * gap );
  2689. return;
  2690. }
  2691. // Don't have really long phonemes
  2692. gap = min( gap, DEFAULT_PHONEME_LENGTH );
  2693. CWordTag *word = m_Tags.GetWordForPhoneme( cp );
  2694. if ( !word )
  2695. {
  2696. Con_Printf( "EditInsertPhonemeBefore: phoneme not a member of any known word!!!\n" );
  2697. return;
  2698. }
  2699. int clicked = word->IndexOfPhoneme( cp );
  2700. if ( clicked < 0 )
  2701. {
  2702. Con_Printf( "EditInsertPhonemeBefore: phoneme not a member of any specified word!!!\n" );
  2703. Assert( 0 );
  2704. return;
  2705. }
  2706. CPhonemeTag phoneme;
  2707. CPhonemeParams params;
  2708. memset( &params, 0, sizeof( params ) );
  2709. strcpy( params.m_szDialogTitle, "Phoneme/Viseme Properties" );
  2710. strcpy( params.m_szName, "" );
  2711. int iret = PhonemeProperties( &params );
  2712. SetFocus( (HWND)getHandle() );
  2713. if ( !iret )
  2714. {
  2715. return;
  2716. }
  2717. SetDirty( true );
  2718. PushUndo();
  2719. phoneme.SetPhonemeCode( TextToPhoneme( params.m_szName ) );
  2720. phoneme.SetTag( params.m_szName );
  2721. phoneme.SetEndTime( cp->GetStartTime() );
  2722. phoneme.SetStartTime( cp->GetStartTime() - gap );
  2723. phoneme.m_bSelected = true;
  2724. cp->m_bSelected = false;
  2725. word->m_Phonemes.InsertBefore( clicked, new CPhonemeTag( phoneme ) );
  2726. PushRedo();
  2727. // Add it
  2728. redraw();
  2729. }
  2730. //-----------------------------------------------------------------------------
  2731. // Purpose:
  2732. //-----------------------------------------------------------------------------
  2733. void PhonemeEditor::EditInsertPhonemeAfter( void )
  2734. {
  2735. if ( GetMode() != MODE_PHONEMES )
  2736. return;
  2737. CPhonemeTag *cp = GetSelectedPhoneme();
  2738. if ( !cp )
  2739. return;
  2740. float gap = GetTimeGapToNextPhoneme( true, cp );
  2741. if ( gap < MINIMUM_PHONEME_GAP )
  2742. {
  2743. Con_Printf( "Can't insert after, gap of %.2f ms is too small\n", 1000.0f * gap );
  2744. return;
  2745. }
  2746. // Don't have really long phonemes
  2747. gap = min( gap, DEFAULT_PHONEME_LENGTH );
  2748. CWordTag *word = m_Tags.GetWordForPhoneme( cp );
  2749. if ( !word )
  2750. {
  2751. Con_Printf( "EditInsertPhonemeAfter: phoneme not a member of any known word!!!\n" );
  2752. return;
  2753. }
  2754. int clicked = word->IndexOfPhoneme( cp );
  2755. if ( clicked < 0 )
  2756. {
  2757. Con_Printf( "EditInsertPhonemeAfter: phoneme not a member of any specified word!!!\n" );
  2758. Assert( 0 );
  2759. return;
  2760. }
  2761. CPhonemeTag phoneme;
  2762. CPhonemeParams params;
  2763. memset( &params, 0, sizeof( params ) );
  2764. strcpy( params.m_szDialogTitle, "Phoneme/Viseme Properties" );
  2765. strcpy( params.m_szName, "" );
  2766. int iret = PhonemeProperties( &params );
  2767. SetFocus( (HWND)getHandle() );
  2768. if ( !iret )
  2769. {
  2770. return;
  2771. }
  2772. SetDirty( true );
  2773. PushUndo();
  2774. phoneme.SetPhonemeCode( TextToPhoneme( params.m_szName ) );
  2775. phoneme.SetTag( params.m_szName );
  2776. phoneme.SetEndTime( cp->GetEndTime() + gap );
  2777. phoneme.SetStartTime( cp->GetEndTime() );
  2778. phoneme.m_bSelected = true;
  2779. cp->m_bSelected = false;
  2780. word->m_Phonemes.InsertAfter( clicked, new CPhonemeTag( phoneme ) );
  2781. PushRedo();
  2782. // Add it
  2783. redraw();
  2784. }
  2785. //-----------------------------------------------------------------------------
  2786. // Purpose:
  2787. //-----------------------------------------------------------------------------
  2788. void PhonemeEditor::EditInsertWordBefore( void )
  2789. {
  2790. if ( GetMode() != MODE_PHONEMES )
  2791. return;
  2792. CWordTag *cw = GetSelectedWord();
  2793. if ( !cw )
  2794. return;
  2795. float gap = GetTimeGapToNextWord( false, cw );
  2796. if ( gap < MINIMUM_WORD_GAP )
  2797. {
  2798. Con_Printf( "Can't insert before, gap of %.2f ms is too small\n", 1000.0f * gap );
  2799. return;
  2800. }
  2801. // Don't have really long words
  2802. gap = min( gap, DEFAULT_WORD_LENGTH );
  2803. int clicked = IndexOfWord( cw );
  2804. if ( clicked < 0 )
  2805. {
  2806. Con_Printf( "EditInsertWordBefore: word not in sentence!!!\n" );
  2807. Assert( 0 );
  2808. return;
  2809. }
  2810. CInputParams params;
  2811. memset( &params, 0, sizeof( params ) );
  2812. strcpy( params.m_szDialogTitle, "Insert Word" );
  2813. strcpy( params.m_szPrompt, "Word:" );
  2814. strcpy( params.m_szInputText, "" );
  2815. params.m_nLeft = -1;
  2816. params.m_nTop = -1;
  2817. params.m_bPositionDialog = true;
  2818. if ( params.m_bPositionDialog )
  2819. {
  2820. RECT rcWord;
  2821. GetWordRect( cw, rcWord );
  2822. // Convert to screen coords
  2823. POINT pt;
  2824. pt.x = rcWord.left;
  2825. pt.y = rcWord.top;
  2826. ClientToScreen( (HWND)getHandle(), &pt );
  2827. params.m_nLeft = pt.x;
  2828. params.m_nTop = pt.y;
  2829. }
  2830. int iret = InputProperties( &params );
  2831. SetFocus( (HWND)getHandle() );
  2832. if ( !iret )
  2833. {
  2834. return;
  2835. }
  2836. if ( strlen( params.m_szInputText ) <= 0 )
  2837. {
  2838. return;
  2839. }
  2840. int wordCount = CSentence::CountWords( params.m_szInputText );
  2841. if ( wordCount > 1 )
  2842. {
  2843. Con_Printf( "Can only insert one word at a time, %s has %i words in it!\n",
  2844. params.m_szInputText, wordCount );
  2845. return;
  2846. }
  2847. SetDirty( true );
  2848. PushUndo();
  2849. CWordTag newword;
  2850. newword.SetWord( params.m_szInputText );
  2851. newword.m_flEndTime = cw->m_flStartTime;
  2852. newword.m_flStartTime = cw->m_flStartTime - gap;
  2853. newword.m_bSelected = true;
  2854. cw->m_bSelected = false;
  2855. m_Tags.m_Words.InsertBefore( clicked, new CWordTag( newword ) );
  2856. PushRedo();
  2857. // Add it
  2858. redraw();
  2859. // Jump to phoneme insertion UI
  2860. EditInsertFirstPhonemeOfWord();
  2861. }
  2862. //-----------------------------------------------------------------------------
  2863. // Purpose:
  2864. //-----------------------------------------------------------------------------
  2865. void PhonemeEditor::EditInsertWordAfter( void )
  2866. {
  2867. if ( GetMode() != MODE_PHONEMES )
  2868. return;
  2869. CWordTag *cw = GetSelectedWord();
  2870. if ( !cw )
  2871. return;
  2872. float gap = GetTimeGapToNextWord( true, cw );
  2873. if ( gap < MINIMUM_WORD_GAP )
  2874. {
  2875. Con_Printf( "Can't insert after, gap of %.2f ms is too small\n", 1000.0f * gap );
  2876. return;
  2877. }
  2878. // Don't have really long words
  2879. gap = min( gap, DEFAULT_WORD_LENGTH );
  2880. int clicked = IndexOfWord( cw );
  2881. if ( clicked < 0 )
  2882. {
  2883. Con_Printf( "EditInsertWordBefore: word not in sentence!!!\n" );
  2884. Assert( 0 );
  2885. return;
  2886. }
  2887. CInputParams params;
  2888. memset( &params, 0, sizeof( params ) );
  2889. strcpy( params.m_szDialogTitle, "Insert Word" );
  2890. strcpy( params.m_szPrompt, "Word:" );
  2891. strcpy( params.m_szInputText, "" );
  2892. params.m_nLeft = -1;
  2893. params.m_nTop = -1;
  2894. params.m_bPositionDialog = true;
  2895. if ( params.m_bPositionDialog )
  2896. {
  2897. RECT rcWord;
  2898. GetWordRect( cw, rcWord );
  2899. // Convert to screen coords
  2900. POINT pt;
  2901. pt.x = rcWord.left;
  2902. pt.y = rcWord.top;
  2903. ClientToScreen( (HWND)getHandle(), &pt );
  2904. params.m_nLeft = pt.x;
  2905. params.m_nTop = pt.y;
  2906. }
  2907. int iret = InputProperties( &params );
  2908. SetFocus( (HWND)getHandle() );
  2909. if ( !iret )
  2910. {
  2911. return;
  2912. }
  2913. if ( strlen( params.m_szInputText ) <= 0 )
  2914. {
  2915. return;
  2916. }
  2917. int wordCount = CSentence::CountWords( params.m_szInputText );
  2918. if ( wordCount > 1 )
  2919. {
  2920. Con_Printf( "Can only insert one word at a time, %s has %i words in it!\n",
  2921. params.m_szInputText, wordCount );
  2922. return;
  2923. }
  2924. SetDirty( true );
  2925. PushUndo();
  2926. CWordTag newword;
  2927. newword.SetWord( params.m_szInputText );
  2928. newword.m_flEndTime = cw->m_flEndTime + gap;
  2929. newword.m_flStartTime = cw->m_flEndTime;
  2930. newword.m_bSelected = true;
  2931. cw->m_bSelected = false;
  2932. CWordTag *w = new CWordTag( newword );
  2933. Assert( w );
  2934. if ( w )
  2935. {
  2936. m_Tags.m_Words.InsertAfter( clicked, w );
  2937. }
  2938. PushRedo();
  2939. // Add it
  2940. redraw();
  2941. EditInsertFirstPhonemeOfWord();
  2942. }
  2943. //-----------------------------------------------------------------------------
  2944. // Purpose:
  2945. //-----------------------------------------------------------------------------
  2946. void PhonemeEditor::EditDeletePhoneme( void )
  2947. {
  2948. if ( GetMode() != MODE_PHONEMES )
  2949. return;
  2950. CountSelected();
  2951. if ( m_nSelectedPhonemeCount < 1 )
  2952. {
  2953. return;
  2954. }
  2955. SetDirty( true );
  2956. PushUndo();
  2957. for ( int i = m_Tags.m_Words.Count() - 1; i >= 0; i-- )
  2958. {
  2959. CWordTag *word = m_Tags.m_Words[ i ];
  2960. if ( !word )
  2961. continue;
  2962. for ( int j = word->m_Phonemes.Count() - 1; j >= 0; j-- )
  2963. {
  2964. CPhonemeTag *p = word->m_Phonemes[ j ];
  2965. if ( !p || !p->m_bSelected )
  2966. continue;
  2967. // Delete it
  2968. word->m_Phonemes.Remove( j );
  2969. }
  2970. }
  2971. PushRedo();
  2972. redraw();
  2973. }
  2974. //-----------------------------------------------------------------------------
  2975. // Purpose:
  2976. //-----------------------------------------------------------------------------
  2977. void PhonemeEditor::EditDeleteWord( void )
  2978. {
  2979. if ( GetMode() != MODE_PHONEMES )
  2980. return;
  2981. CountSelected();
  2982. if ( m_nSelectedWordCount < 1 )
  2983. {
  2984. return;
  2985. }
  2986. SetDirty( true );
  2987. PushUndo();
  2988. for ( int i = m_Tags.m_Words.Count() - 1; i >= 0; i-- )
  2989. {
  2990. CWordTag *word = m_Tags.m_Words[ i ];
  2991. if ( !word || !word->m_bSelected )
  2992. continue;
  2993. m_Tags.m_Words.Remove( i );
  2994. }
  2995. PushRedo();
  2996. redraw();
  2997. }
  2998. //-----------------------------------------------------------------------------
  2999. // Purpose:
  3000. //-----------------------------------------------------------------------------
  3001. void PhonemeEditor::PlayEditedWave( bool selection /* = false */ )
  3002. {
  3003. StopPlayback();
  3004. if ( !m_pWaveFile )
  3005. return;
  3006. // Make sure phonemes are loaded
  3007. FacePoser_EnsurePhonemesLoaded();
  3008. SaveLinguisticData();
  3009. SetScrubTime( 0.0f );
  3010. SetScrubTargetTime( m_pWaveFile->GetRunningLength() );
  3011. }
  3012. typedef struct channel_s
  3013. {
  3014. int leftvol;
  3015. int rightvol;
  3016. int rleftvol;
  3017. int rrightvol;
  3018. float pitch;
  3019. } channel_t;
  3020. bool PhonemeEditor::CreateCroppedWave( char const *filename, int startsample, int endsample )
  3021. {
  3022. Assert( sound );
  3023. CAudioWaveOutput *pWaveOutput = ( CAudioWaveOutput * )sound->GetAudioOutput();
  3024. if ( !pWaveOutput )
  3025. return false;
  3026. CAudioSource *wave = sound->LoadSound( m_WorkFile.m_szWaveFile );
  3027. if ( !wave )
  3028. return false;
  3029. CAudioMixer *pMixer = wave->CreateMixer();
  3030. if ( !pMixer )
  3031. return false;
  3032. // Create out put file
  3033. OutFileRIFF riffout( filename, io_out );
  3034. // Create output iterator
  3035. IterateOutputRIFF store( riffout );
  3036. WAVEFORMATEX format;
  3037. format.cbSize = sizeof( format );
  3038. format.wFormatTag = WAVE_FORMAT_PCM;
  3039. format.nAvgBytesPerSec = (int)wave->SampleRate();
  3040. format.nChannels = 1;
  3041. format.wBitsPerSample = 8;
  3042. format.nSamplesPerSec = (int)wave->SampleRate();
  3043. format.nBlockAlign = 1; // (int)wave->SampleSize();
  3044. store.ChunkWrite( WAVE_FMT, &format, sizeof( format ) );
  3045. // Pull in data and write it out
  3046. int currentsample = 0;
  3047. store.ChunkStart( WAVE_DATA );
  3048. // need a bit of space
  3049. short samples[ 2 ];
  3050. channel_t channel;
  3051. channel.leftvol = 255;
  3052. channel.rightvol = 255;
  3053. channel.pitch = 1.0;
  3054. while ( 1 )
  3055. {
  3056. pWaveOutput->m_audioDevice.MixBegin();
  3057. if ( !pMixer->MixDataToDevice( &pWaveOutput->m_audioDevice, &channel, currentsample, 1, wave->SampleRate(), true ) )
  3058. break;
  3059. pWaveOutput->m_audioDevice.TransferBufferStereo16( samples, 1 );
  3060. currentsample = pMixer->GetSamplePosition();
  3061. if ( currentsample >= startsample && currentsample <= endsample )
  3062. {
  3063. // left + right (2 channels ) * 16 bits
  3064. float s1 = (float)( samples[ 0 ] >> 8 );
  3065. float s2 = (float)( samples[ 1 ] >> 8 );
  3066. float avg = ( s1 + s2 ) / 2.0f;
  3067. unsigned char chopped = (unsigned char)( avg + 127.0f );
  3068. store.ChunkWriteData( &chopped, sizeof( byte ) );
  3069. }
  3070. }
  3071. store.ChunkFinish();
  3072. delete pMixer;
  3073. delete wave;
  3074. return true;
  3075. }
  3076. void PhonemeEditor::SentenceFromString( CSentence& sentence, char const *str )
  3077. {
  3078. sentence.Reset();
  3079. if ( !str || !str[0] || CSentence::CountWords( str ) == 0 )
  3080. {
  3081. return;
  3082. }
  3083. char word[ 256 ];
  3084. unsigned char const *in = (unsigned char *)str;
  3085. char *out = word;
  3086. while ( *in )
  3087. {
  3088. if ( *in > 32 )
  3089. {
  3090. *out++ = *in++;
  3091. }
  3092. else
  3093. {
  3094. *out = 0;
  3095. while ( *in && *in <= 32 )
  3096. {
  3097. in++;
  3098. }
  3099. if ( strlen( word ) > 0 )
  3100. {
  3101. CWordTag *w = new CWordTag( (char *)word );
  3102. Assert( w );
  3103. if ( w )
  3104. {
  3105. sentence.m_Words.AddToTail( w );
  3106. }
  3107. }
  3108. out = word;
  3109. }
  3110. }
  3111. *out = 0;
  3112. if ( strlen( word ) > 0 )
  3113. {
  3114. CWordTag *w = new CWordTag( (char *)word );
  3115. Assert( w );
  3116. if ( w )
  3117. {
  3118. sentence.m_Words.AddToTail( w );
  3119. }
  3120. }
  3121. sentence.SetText( str );
  3122. }
  3123. //-----------------------------------------------------------------------------
  3124. // Purpose:
  3125. //-----------------------------------------------------------------------------
  3126. void PhonemeEditor::RedoPhonemeExtractionSelected( void )
  3127. {
  3128. if ( GetMode() != MODE_PHONEMES )
  3129. return;
  3130. if ( !CheckSpeechAPI() )
  3131. return;
  3132. if ( !m_pWaveFile )
  3133. {
  3134. Con_Printf( "Can't redo extraction, no wavefile loaded!\n" );
  3135. Assert( 0 );
  3136. return;
  3137. }
  3138. if ( !m_bSelectionActive )
  3139. {
  3140. Con_Printf( "Please select a portion of the .wav from which to re-extract phonemes\n" );
  3141. return;
  3142. }
  3143. // Now copy data back into original list, offsetting by samplestart time
  3144. float numsamples = m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate();
  3145. float selectionstarttime = 0.0f;
  3146. if ( numsamples > 0.0f )
  3147. {
  3148. // Convert sample #'s to time
  3149. selectionstarttime = ( m_nSelection[ 0 ] / numsamples ) * m_pWaveFile->GetRunningLength();
  3150. selectionstarttime = max( 0.0f, selectionstarttime );
  3151. }
  3152. else
  3153. {
  3154. Con_Printf( "Original .wav file %s has no samples!!!\n", m_WorkFile.m_szWaveFile );
  3155. return;
  3156. }
  3157. int i;
  3158. // Create input array of just selected words
  3159. CSentence m_InputWords;
  3160. CSentence m_Results;
  3161. CountSelected();
  3162. bool usingselection = true;
  3163. if ( m_nSelectedWordCount == 0 )
  3164. {
  3165. // Allow user to type in text
  3166. // Build word string
  3167. char wordstring[ 1024 ];
  3168. strcpy( wordstring, "" );
  3169. CInputParams params;
  3170. memset( &params, 0, sizeof( params ) );
  3171. strcpy( params.m_szDialogTitle, "Phrase Word List" );
  3172. strcpy( params.m_szPrompt, "Phrase" );
  3173. strcpy( params.m_szInputText, wordstring );
  3174. if ( !InputProperties( &params ) )
  3175. return;
  3176. if ( strlen( params.m_szInputText ) <= 0 )
  3177. {
  3178. Con_ErrorPrintf( "Edit word list: No words entered!\n" );
  3179. return;
  3180. }
  3181. SentenceFromString( m_InputWords, params.m_szInputText );
  3182. if ( m_InputWords.m_Words.Count() == 0 )
  3183. {
  3184. Con_Printf( "You must either select words, or type in a set of words in order to extract phonemes!\n" );
  3185. return;
  3186. }
  3187. usingselection = false;
  3188. }
  3189. else
  3190. {
  3191. if ( !AreSelectedWordsContiguous() )
  3192. {
  3193. Con_Printf( "Can only redo extraction on a contiguous subset of words\n" );
  3194. return;
  3195. }
  3196. char temp[ 4096 ];
  3197. bool killspace = false;
  3198. Q_strncpy( temp, m_InputWords.GetText(), sizeof( temp ) );
  3199. // Iterate existing words, looking for contiguous selected words
  3200. for ( i = 0; i < m_Tags.m_Words.Count(); i++ )
  3201. {
  3202. CWordTag *word = m_Tags.m_Words[ i ];
  3203. if ( !word || !word->m_bSelected )
  3204. continue;
  3205. // Now add "clean slate" to input list
  3206. m_InputWords.m_Words.AddToTail( new CWordTag( *word ) );
  3207. Q_strncat( temp, word->GetWord(), sizeof( temp ), COPY_ALL_CHARACTERS );
  3208. Q_strncat( temp, " ", sizeof( temp ), COPY_ALL_CHARACTERS );
  3209. killspace = true;
  3210. }
  3211. // Kill terminal space character
  3212. int len = Q_strlen( temp );
  3213. if ( killspace && ( len >= 1 ) )
  3214. {
  3215. Assert( temp[ len -1 ] == ' ' );
  3216. temp[ len - 1 ] = 0;
  3217. }
  3218. m_InputWords.SetText( temp );
  3219. }
  3220. m_nLastExtractionResult = SR_RESULT_NORESULT;
  3221. char szCroppedFile[ 512 ];
  3222. char szBaseFile[ 512 ];
  3223. Q_StripExtension( m_WorkFile.m_szWaveFile, szBaseFile, sizeof( szBaseFile ) );
  3224. Q_snprintf( szCroppedFile, sizeof( szCroppedFile ), "%s%s_work1.wav", m_WorkFile.m_szBasePath, szBaseFile );
  3225. filesystem->RemoveFile( szCroppedFile, "GAME" );
  3226. if ( !CreateCroppedWave( szCroppedFile, m_nSelection[ 0 ], m_nSelection[ 1 ] ) )
  3227. {
  3228. Con_Printf( "Unable to create cropped wave file %s from samples %i to %i\n",
  3229. szCroppedFile,
  3230. m_nSelection[ 0 ],
  3231. m_nSelection[ 1 ] );
  3232. return;
  3233. }
  3234. CAudioSource *m_pCroppedWave = sound->LoadSound( szCroppedFile );
  3235. if ( !m_pCroppedWave )
  3236. {
  3237. Con_Printf( "Unable to load cropped wave file %s from samples %i to %i\n" );
  3238. return;
  3239. }
  3240. // Save any pending stuff
  3241. SaveLinguisticData();
  3242. // Store off copy of complete sentence
  3243. m_TagsExt = m_Tags;
  3244. char filename[ 512 ];
  3245. Q_snprintf( filename, sizeof( filename ), "%s%s", m_WorkFile.m_szBasePath, szCroppedFile );
  3246. m_nLastExtractionResult = m_pPhonemeExtractor->Extract(
  3247. filename,
  3248. (int)( m_pCroppedWave->GetRunningLength() * m_pCroppedWave->SampleRate() * m_pCroppedWave->TrueSampleSize() ),
  3249. Con_Printf,
  3250. m_InputWords,
  3251. m_Results );
  3252. if ( m_InputWords.m_Words.Count() != m_Results.m_Words.Count() )
  3253. {
  3254. Con_Printf( "Extraction returned %i words, source had %i, try adjusting selection\n",
  3255. m_Results.m_Words.Count(), m_InputWords.m_Words.Count() );
  3256. filesystem->RemoveFile( filename, "GAME" );
  3257. redraw();
  3258. return;
  3259. }
  3260. float bytespersecond = m_pCroppedWave->SampleRate() * m_pCroppedWave->TrueSampleSize();
  3261. // Tracker 57389:
  3262. // Total hack to fix a bug where the Lipsinc extractor is messing up the # channels on 16 bit stereo waves
  3263. if ( m_pPhonemeExtractor->GetAPIType() == SPEECH_API_LIPSINC &&
  3264. m_pCroppedWave->IsStereoWav() &&
  3265. m_pCroppedWave->SampleSize() == 16 )
  3266. {
  3267. bytespersecond *= 2.0f;
  3268. }
  3269. // Now convert byte offsets to times
  3270. for ( i = 0; i < m_Results.m_Words.Count(); i++ )
  3271. {
  3272. CWordTag *tag = m_Results.m_Words[ i ];
  3273. Assert( tag );
  3274. if ( !tag )
  3275. continue;
  3276. tag->m_flStartTime = ( float )(tag->m_uiStartByte ) / bytespersecond;
  3277. tag->m_flEndTime = ( float )(tag->m_uiEndByte ) / bytespersecond;
  3278. for ( int j = 0; j < tag->m_Phonemes.Count(); j++ )
  3279. {
  3280. CPhonemeTag *ptag = tag->m_Phonemes[ j ];
  3281. Assert( ptag );
  3282. if ( !ptag )
  3283. continue;
  3284. ptag->SetStartTime( ( float )(ptag->m_uiStartByte ) / bytespersecond );
  3285. ptag->SetEndTime( ( float )(ptag->m_uiEndByte ) / bytespersecond );
  3286. }
  3287. }
  3288. if ( usingselection )
  3289. {
  3290. // Copy data into m_TagsExt, offseting times by selectionstarttime
  3291. CWordTag *from;
  3292. CWordTag *to;
  3293. int fromWord = 0;
  3294. for ( i = 0; i < m_TagsExt.m_Words.Count() ; i++ )
  3295. {
  3296. to = m_TagsExt.m_Words[ i ];
  3297. if ( !to || !to->m_bSelected )
  3298. continue;
  3299. // Found start of contiguous run
  3300. if ( fromWord >= m_Results.m_Words.Count() )
  3301. break;
  3302. from = m_Results.m_Words[ fromWord++ ];
  3303. Assert( from );
  3304. if ( !from )
  3305. continue;
  3306. // Remove all phonemes from destination
  3307. while ( to->m_Phonemes.Count() > 0 )
  3308. {
  3309. CPhonemeTag *p = to->m_Phonemes[ 0 ];
  3310. Assert( p );
  3311. to->m_Phonemes.Remove( 0 );
  3312. delete p;
  3313. }
  3314. // Now copy phonemes from source
  3315. for ( int j = 0; j < from->m_Phonemes.Count(); j++ )
  3316. {
  3317. CPhonemeTag *fromPhoneme = from->m_Phonemes[ j ];
  3318. Assert( fromPhoneme );
  3319. if ( !fromPhoneme )
  3320. continue;
  3321. CPhonemeTag newPhoneme( *fromPhoneme );
  3322. // Offset start time
  3323. newPhoneme.AddStartTime( selectionstarttime );
  3324. newPhoneme.AddEndTime( selectionstarttime );
  3325. // Add it back in with corrected timing data
  3326. CPhonemeTag *p = new CPhonemeTag( newPhoneme );
  3327. Assert( p );
  3328. if ( p )
  3329. {
  3330. to->m_Phonemes.AddToTail( p );
  3331. }
  3332. }
  3333. // Done
  3334. if ( fromWord >= m_Results.m_Words.Count() )
  3335. break;
  3336. }
  3337. }
  3338. else
  3339. {
  3340. // Find word just before starting point of selection and
  3341. // place input words into list starting that that point
  3342. int startWord = 0;
  3343. CWordTag *firstWordOfPhrase = m_Results.m_Words[ 0 ];
  3344. Assert( firstWordOfPhrase );
  3345. for ( ; startWord < m_TagsExt.m_Words.Count(); startWord++ )
  3346. {
  3347. CWordTag *w = m_TagsExt.m_Words[ startWord ];
  3348. Assert( w );
  3349. if ( !w )
  3350. continue;
  3351. if ( w->m_flStartTime > firstWordOfPhrase->m_flStartTime + selectionstarttime )
  3352. break;
  3353. }
  3354. for ( i = 0; i < m_Results.m_Words.Count(); i++ )
  3355. {
  3356. CWordTag *from = m_Results.m_Words[ i ];
  3357. Assert( from );
  3358. if ( !from )
  3359. continue;
  3360. CWordTag *to = new CWordTag( *from );
  3361. Assert( to );
  3362. to->m_flStartTime += selectionstarttime;
  3363. to->m_flEndTime += selectionstarttime;
  3364. // Now adjust phoneme times
  3365. for ( int j = 0; j < to->m_Phonemes.Count(); j++ )
  3366. {
  3367. CPhonemeTag *toPhoneme = to->m_Phonemes[ j ];
  3368. Assert( toPhoneme );
  3369. if ( !toPhoneme )
  3370. continue;
  3371. // Offset start time
  3372. toPhoneme->AddStartTime( selectionstarttime );
  3373. toPhoneme->AddEndTime( selectionstarttime );
  3374. }
  3375. m_TagsExt.m_Words.InsertBefore( startWord++, to );
  3376. }
  3377. }
  3378. Con_Printf( "Cleaning up...\n" );
  3379. filesystem->RemoveFile( filename, "GAME" );
  3380. SetFocus( (HWND)getHandle() );
  3381. redraw();
  3382. }
  3383. void PhonemeEditor::RedoPhonemeExtraction( void )
  3384. {
  3385. if ( GetMode() != MODE_PHONEMES )
  3386. return;
  3387. if ( !CheckSpeechAPI() )
  3388. return;
  3389. m_nLastExtractionResult = SR_RESULT_NORESULT;
  3390. if ( !m_pWaveFile )
  3391. return;
  3392. SaveLinguisticData();
  3393. // Send m_WorkFile.m_szWorkingFile to extractor and retrieve resulting data
  3394. //
  3395. m_TagsExt.Reset();
  3396. Assert( m_pPhonemeExtractor );
  3397. char filename[ 512 ];
  3398. Q_snprintf( filename, sizeof( filename ), "%s%s", m_WorkFile.m_szBasePath, m_WorkFile.m_szWorkingFile );
  3399. m_nLastExtractionResult = m_pPhonemeExtractor->Extract(
  3400. filename,
  3401. (int)( m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate() * m_pWaveFile->TrueSampleSize() ),
  3402. Con_Printf,
  3403. m_Tags,
  3404. m_TagsExt );
  3405. float bytespersecond = m_pWaveFile->SampleRate() * m_pWaveFile->TrueSampleSize();
  3406. // Now convert byte offsets to times
  3407. int i;
  3408. for ( i = 0; i < m_TagsExt.m_Words.Count(); i++ )
  3409. {
  3410. CWordTag *tag = m_TagsExt.m_Words[ i ];
  3411. Assert( tag );
  3412. if ( !tag )
  3413. continue;
  3414. tag->m_flStartTime = ( float )(tag->m_uiStartByte ) / bytespersecond;
  3415. tag->m_flEndTime = ( float )(tag->m_uiEndByte ) / bytespersecond;
  3416. for ( int j = 0; j < tag->m_Phonemes.Count(); j++ )
  3417. {
  3418. CPhonemeTag *ptag = tag->m_Phonemes[ j ];
  3419. Assert( ptag );
  3420. if ( !ptag )
  3421. continue;
  3422. ptag->SetStartTime( ( float )(ptag->m_uiStartByte ) / bytespersecond );
  3423. ptag->SetEndTime( ( float )(ptag->m_uiEndByte ) / bytespersecond );
  3424. }
  3425. }
  3426. SetFocus( (HWND)getHandle() );
  3427. redraw();
  3428. }
  3429. //-----------------------------------------------------------------------------
  3430. // Purpose:
  3431. //-----------------------------------------------------------------------------
  3432. void PhonemeEditor::Deselect( void )
  3433. {
  3434. m_nSelection[ 0 ] = m_nSelection[ 1 ] = 0;
  3435. m_bSelectionActive = false;
  3436. }
  3437. void PhonemeEditor::ITER_SelectSpanningWords( CWordTag *word, float amount )
  3438. {
  3439. Assert( word );
  3440. word->m_bSelected = false;
  3441. if ( !m_bSelectionActive )
  3442. return;
  3443. if ( !m_pWaveFile )
  3444. return;
  3445. float numsamples = m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate();
  3446. if ( numsamples > 0.0f )
  3447. {
  3448. // Convert sample #'s to time
  3449. float starttime = ( m_nSelection[ 0 ] / numsamples ) * m_pWaveFile->GetRunningLength();
  3450. float endtime = ( m_nSelection[ 1 ] / numsamples ) * m_pWaveFile->GetRunningLength();
  3451. if ( word->m_flEndTime >= starttime &&
  3452. word->m_flStartTime <= endtime )
  3453. {
  3454. word->m_bSelected = true;
  3455. m_bWordsActive = true;
  3456. }
  3457. }
  3458. }
  3459. //-----------------------------------------------------------------------------
  3460. // Purpose:
  3461. // Input : start -
  3462. // end -
  3463. //-----------------------------------------------------------------------------
  3464. void PhonemeEditor::SelectSamples( int start, int end )
  3465. {
  3466. if ( !m_pWaveFile )
  3467. return;
  3468. // Make sure order is correct
  3469. if ( end < start )
  3470. {
  3471. int temp = end;
  3472. end = start;
  3473. start = temp;
  3474. }
  3475. Deselect();
  3476. m_nSelection[ 0 ] = start;
  3477. m_nSelection[ 1 ] = end;
  3478. m_bSelectionActive = true;
  3479. // Select any words that span the selection
  3480. //
  3481. TraverseWords( &PhonemeEditor::ITER_SelectSpanningWords, 0.0f );
  3482. redraw();
  3483. }
  3484. void PhonemeEditor::FinishMoveSelection( int startx, int mx )
  3485. {
  3486. if ( !m_pWaveFile )
  3487. return;
  3488. int sampleStart = GetSampleForMouse( startx );
  3489. int sampleEnd = GetSampleForMouse( mx );
  3490. int delta = sampleEnd - sampleStart;
  3491. for ( int i = 0; i < 2; i++ )
  3492. {
  3493. m_nSelection[ i ] += delta;
  3494. }
  3495. // Select any words that span the selection
  3496. //
  3497. TraverseWords( &PhonemeEditor::ITER_SelectSpanningWords, 0.0f );
  3498. redraw();
  3499. }
  3500. void PhonemeEditor::FinishMoveSelectionStart( int startx, int mx )
  3501. {
  3502. if ( !m_pWaveFile )
  3503. return;
  3504. int sampleStart = GetSampleForMouse( startx );
  3505. int sampleEnd = GetSampleForMouse( mx );
  3506. int delta = sampleEnd - sampleStart;
  3507. m_nSelection[ 0 ] += delta;
  3508. if ( m_nSelection[ 0 ] >= m_nSelection[ 1 ] )
  3509. {
  3510. Deselect();
  3511. }
  3512. // Select any words that span the selection
  3513. //
  3514. TraverseWords( &PhonemeEditor::ITER_SelectSpanningWords, 0.0f );
  3515. redraw();
  3516. }
  3517. void PhonemeEditor::FinishMoveSelectionEnd( int startx, int mx )
  3518. {
  3519. if ( !m_pWaveFile )
  3520. return;
  3521. int sampleStart = GetSampleForMouse( startx );
  3522. int sampleEnd = GetSampleForMouse( mx );
  3523. int delta = sampleEnd - sampleStart;
  3524. m_nSelection[ 1 ] += delta;
  3525. if ( m_nSelection[ 1 ] <= m_nSelection[ 0 ] )
  3526. {
  3527. Deselect();
  3528. }
  3529. // Select any words that span the selection
  3530. //
  3531. TraverseWords( &PhonemeEditor::ITER_SelectSpanningWords, 0.0f );
  3532. redraw();
  3533. }
  3534. //-----------------------------------------------------------------------------
  3535. // Purpose:
  3536. // Input : startx -
  3537. // mx -
  3538. //-----------------------------------------------------------------------------
  3539. void PhonemeEditor::FinishSelect( int startx, int mx )
  3540. {
  3541. if ( !m_pWaveFile )
  3542. return;
  3543. // Don't select really small areas
  3544. if ( abs( startx - mx ) < 2 )
  3545. return;
  3546. int sampleStart = GetSampleForMouse( startx );
  3547. int sampleEnd = GetSampleForMouse( mx );
  3548. SelectSamples( sampleStart, sampleEnd );
  3549. }
  3550. //-----------------------------------------------------------------------------
  3551. // Purpose:
  3552. // Input : mx -
  3553. // my -
  3554. // Output : Returns true on success, false on failure.
  3555. //-----------------------------------------------------------------------------
  3556. bool PhonemeEditor::IsMouseOverSamples( int mx, int my )
  3557. {
  3558. if ( GetMode() != MODE_PHONEMES )
  3559. return false;
  3560. // Deterime if phoneme boundary is under the cursor
  3561. //
  3562. if ( !m_pWaveFile )
  3563. return false;
  3564. RECT rc;
  3565. GetWorkspaceRect( rc );
  3566. // Over tag
  3567. if ( my >= TAG_TOP && my <= TAG_BOTTOM )
  3568. return false;
  3569. if ( IsMouseOverPhonemeRow( my ) )
  3570. return false;
  3571. if ( IsMouseOverWordRow( my ) )
  3572. return false;
  3573. RECT rcWord;
  3574. GetWordTrayTopBottom( rcWord );
  3575. RECT rcPhoneme;
  3576. GetPhonemeTrayTopBottom( rcPhoneme );
  3577. if ( my < rcWord.bottom )
  3578. return false;
  3579. if ( my > rcPhoneme.top )
  3580. return false;
  3581. return true;
  3582. }
  3583. void PhonemeEditor::GetScreenStartAndEndTime( float &starttime, float& endtime )
  3584. {
  3585. starttime = m_nLeftOffset / GetPixelsPerSecond();
  3586. endtime = w2() / GetPixelsPerSecond() + starttime;
  3587. }
  3588. float PhonemeEditor::GetTimePerPixel( void )
  3589. {
  3590. RECT rc;
  3591. GetWorkspaceRect( rc );
  3592. float starttime, endtime;
  3593. GetScreenStartAndEndTime( starttime, endtime );
  3594. if ( rc.right - rc.left <= 0 )
  3595. {
  3596. return ( endtime - starttime );
  3597. }
  3598. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  3599. return timeperpixel;
  3600. }
  3601. int PhonemeEditor::GetPixelForSample( int sample )
  3602. {
  3603. RECT rc;
  3604. GetWorkspaceRect( rc );
  3605. if ( !m_pWaveFile )
  3606. return rc.left;
  3607. // Determine start/stop positions
  3608. int totalsamples = (int)( m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate() );
  3609. if ( totalsamples <= 0 )
  3610. {
  3611. return rc.left;
  3612. }
  3613. float starttime, endtime;
  3614. GetScreenStartAndEndTime( starttime, endtime );
  3615. float sampleFrac = (float)sample / (float)totalsamples;
  3616. float sampleTime = sampleFrac * (float)m_pWaveFile->GetRunningLength();
  3617. if ( endtime - starttime < 0.0f )
  3618. {
  3619. return rc.left;
  3620. }
  3621. float windowFrac = ( sampleTime - starttime ) / ( endtime - starttime );
  3622. return rc.left + (int)( windowFrac * ( rc.right - rc.left ) );
  3623. }
  3624. int PhonemeEditor::GetSampleForMouse( int mx )
  3625. {
  3626. if ( !m_pWaveFile )
  3627. return 0;
  3628. RECT rc;
  3629. GetWorkspaceRect( rc );
  3630. // Determine start/stop positions
  3631. int totalsamples = (int)( m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate() );
  3632. float starttime, endtime;
  3633. GetScreenStartAndEndTime( starttime, endtime );
  3634. if ( GetPixelsPerSecond() <= 0 )
  3635. return 0;
  3636. // Start and end times
  3637. float clickTime = (float)mx / GetPixelsPerSecond() + starttime;
  3638. // What sample do these correspond to
  3639. if ( (float)m_pWaveFile->GetRunningLength() <= 0.0f )
  3640. return 0;
  3641. int sampleNumber = (int) ( (float)totalsamples * clickTime / (float)m_pWaveFile->GetRunningLength() );
  3642. return sampleNumber;
  3643. }
  3644. //-----------------------------------------------------------------------------
  3645. // Purpose:
  3646. // Input : mx -
  3647. // my -
  3648. // Output : Returns true on success, false on failure.
  3649. //-----------------------------------------------------------------------------
  3650. bool PhonemeEditor::IsMouseOverSelection( int mx, int my )
  3651. {
  3652. if ( GetMode() != MODE_PHONEMES )
  3653. return false;
  3654. if ( !m_pWaveFile )
  3655. return false;
  3656. if ( !m_bSelectionActive )
  3657. return false;
  3658. if ( !IsMouseOverSamples( mx, my ) )
  3659. return false;
  3660. int sampleNumber = GetSampleForMouse( mx );
  3661. if ( sampleNumber >= m_nSelection[ 0 ] - 3 &&
  3662. sampleNumber <= m_nSelection[ 1 ] + 3 )
  3663. {
  3664. return true;
  3665. }
  3666. return false;
  3667. }
  3668. bool PhonemeEditor::IsMouseOverSelectionStartEdge( mxEvent *event )
  3669. {
  3670. if ( GetMode() != MODE_PHONEMES )
  3671. return false;
  3672. if ( !m_pWaveFile )
  3673. return false;
  3674. int mx, my;
  3675. mx = (short)event->x;
  3676. my = (short)event->y;
  3677. if ( !(event->modifiers & mxEvent::KeyCtrl ) )
  3678. return false;
  3679. if ( !IsMouseOverSelection( mx, my ) )
  3680. return false;
  3681. int sample = GetSampleForMouse( mx );
  3682. int mouse_tolerance = 5;
  3683. RECT rc;
  3684. GetWorkspaceRect( rc );
  3685. // Determine start/stop positions
  3686. float timeperpixel = GetTimePerPixel();
  3687. int samplesperpixel = (int)( timeperpixel * m_pWaveFile->SampleRate() );
  3688. if ( abs( sample - m_nSelection[ 0 ] ) < mouse_tolerance * samplesperpixel )
  3689. {
  3690. return true;
  3691. }
  3692. return false;
  3693. }
  3694. bool PhonemeEditor::IsMouseOverSelectionEndEdge( mxEvent *event )
  3695. {
  3696. if ( GetMode() != MODE_PHONEMES )
  3697. return false;
  3698. if ( !m_pWaveFile )
  3699. return false;
  3700. int mx, my;
  3701. mx = (short)event->x;
  3702. my = (short)event->y;
  3703. if ( !(event->modifiers & mxEvent::KeyCtrl ) )
  3704. return false;
  3705. if ( !IsMouseOverSelection( mx, my ) )
  3706. return false;
  3707. int sample = GetSampleForMouse( mx );
  3708. int mouse_tolerance = 5;
  3709. RECT rc;
  3710. GetWorkspaceRect( rc );
  3711. if ( GetPixelsPerSecond() <= 0.0f )
  3712. return false;
  3713. if ( ( rc.right - rc.left ) <= 0 )
  3714. return false;
  3715. // Determine start/stop positions
  3716. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  3717. float endtime = w2() / GetPixelsPerSecond() + starttime;
  3718. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  3719. int samplesperpixel = (int)( timeperpixel * m_pWaveFile->SampleRate() );
  3720. if ( abs( sample - m_nSelection[ 1 ] ) < mouse_tolerance * samplesperpixel )
  3721. {
  3722. return true;
  3723. }
  3724. return false;
  3725. }
  3726. void PhonemeEditor::OnImport()
  3727. {
  3728. char filename[ 512 ];
  3729. if ( !FacePoser_ShowOpenFileNameDialog( filename, sizeof( filename ), "sound", "*" WORD_DATA_EXTENSION ) )
  3730. {
  3731. return;
  3732. }
  3733. ImportValveDataChunk( filename );
  3734. }
  3735. void PhonemeEditor::OnExport()
  3736. {
  3737. if ( !m_pWaveFile )
  3738. return;
  3739. char filename[ 512 ];
  3740. if ( !FacePoser_ShowSaveFileNameDialog( filename, sizeof( filename ), "sound", "*" WORD_DATA_EXTENSION ) )
  3741. {
  3742. return;
  3743. }
  3744. Q_SetExtension( filename, WORD_DATA_EXTENSION, sizeof( filename ) );
  3745. ExportValveDataChunk( filename );
  3746. }
  3747. //-----------------------------------------------------------------------------
  3748. // Purpose:
  3749. // Input : store -
  3750. //-----------------------------------------------------------------------------
  3751. void PhonemeEditor::StoreValveDataChunk( IterateOutputRIFF& store )
  3752. {
  3753. // Buffer and dump data
  3754. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  3755. m_Tags.SaveToBuffer( buf );
  3756. // Copy into store
  3757. store.ChunkWriteData( buf.Base(), buf.TellPut() );
  3758. }
  3759. //-----------------------------------------------------------------------------
  3760. // Purpose:
  3761. // Input : *tempfile -
  3762. //-----------------------------------------------------------------------------
  3763. void PhonemeEditor::ExportValveDataChunk( char const *tempfile )
  3764. {
  3765. if ( m_Tags.m_Words.Count() <= 0 )
  3766. {
  3767. Con_ErrorPrintf( "PhonemeEditor::ExportValveDataChunk: Sentence has no word data\n" );
  3768. return;
  3769. }
  3770. FileHandle_t fh = filesystem->Open( tempfile, "wb" );
  3771. if ( !fh )
  3772. {
  3773. Con_ErrorPrintf( "PhonemeEditor::ExportValveDataChunk: Unable to write to %s (read-only?)\n", tempfile );
  3774. return;
  3775. }
  3776. else
  3777. {
  3778. // Buffer and dump data
  3779. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  3780. m_Tags.SaveToBuffer( buf );
  3781. filesystem->Write( buf.Base(), buf.TellPut(), fh );
  3782. filesystem->Close(fh);
  3783. Con_Printf( "Exported %i words to %s\n", m_Tags.m_Words.Count(), tempfile );
  3784. }
  3785. }
  3786. //-----------------------------------------------------------------------------
  3787. // Purpose:
  3788. // Input : *tempfile -
  3789. //-----------------------------------------------------------------------------
  3790. void PhonemeEditor::ImportValveDataChunk( char const *tempfile )
  3791. {
  3792. FileHandle_t fh = filesystem->Open( tempfile, "rb" );
  3793. if ( !fh )
  3794. {
  3795. Con_ErrorPrintf( "PhonemeEditor::ImportValveDataChunk: Unable to read from %s\n", tempfile );
  3796. return;
  3797. }
  3798. int len = filesystem->Size( fh );
  3799. if ( len <= 4 )
  3800. {
  3801. Con_ErrorPrintf( "PhonemeEditor::ImportValveDataChunk: File %s has length 0\n", tempfile );
  3802. return;
  3803. }
  3804. ClearExtracted();
  3805. unsigned char *buf = new unsigned char[ len + 1 ];
  3806. filesystem->Read( buf, len, fh );
  3807. filesystem->Close( fh );
  3808. m_TagsExt.InitFromDataChunk( (void *)( buf ), len );
  3809. delete[] buf;
  3810. Con_Printf( "Imported %i words from %s\n", m_TagsExt.m_Words.Count(), tempfile );
  3811. redraw();
  3812. }
  3813. //-----------------------------------------------------------------------------
  3814. // Purpose: Copy file over, but update phoneme lump with new data
  3815. //-----------------------------------------------------------------------------
  3816. void PhonemeEditor::SaveLinguisticData( void )
  3817. {
  3818. if ( !m_pWaveFile )
  3819. return;
  3820. InFileRIFF riff( m_WorkFile.m_szWaveFile, io_in );
  3821. Assert( riff.RIFFName() == RIFF_WAVE );
  3822. // set up the iterator for the whole file (root RIFF is a chunk)
  3823. IterateRIFF walk( riff, riff.RIFFSize() );
  3824. char fullout[ 512 ];
  3825. Q_snprintf( fullout, sizeof( fullout ), "%s%s", m_WorkFile.m_szBasePath, m_WorkFile.m_szWorkingFile );
  3826. OutFileRIFF riffout( fullout, io_out );
  3827. IterateOutputRIFF store( riffout );
  3828. bool formatset = false;
  3829. WAVEFORMATEX format;
  3830. bool wordtrackwritten = false;
  3831. // Walk input chunks and copy to output
  3832. while ( walk.ChunkAvailable() )
  3833. {
  3834. unsigned int originalPos = store.ChunkGetPosition();
  3835. store.ChunkStart( walk.ChunkName() );
  3836. bool skipchunk = false;
  3837. switch ( walk.ChunkName() )
  3838. {
  3839. case WAVE_VALVEDATA:
  3840. // Overwrite data
  3841. StoreValveDataChunk( store );
  3842. wordtrackwritten = true;
  3843. break;
  3844. case WAVE_FMT:
  3845. {
  3846. formatset = true;
  3847. char *buffer = new char[ walk.ChunkSize() ];
  3848. Assert( buffer );
  3849. walk.ChunkRead( buffer );
  3850. format = *(WAVEFORMATEX *)buffer;
  3851. store.ChunkWriteData( buffer, walk.ChunkSize() );
  3852. delete[] buffer;
  3853. }
  3854. break;
  3855. case WAVE_DATA:
  3856. {
  3857. Assert( formatset );
  3858. char *buffer = new char[ walk.ChunkSize() ];
  3859. Assert( buffer );
  3860. walk.ChunkRead( buffer );
  3861. // Resample it
  3862. ResampleChunk( store, (void *)&format, walk.ChunkName(), buffer, walk.ChunkSize() );
  3863. delete[] buffer;
  3864. }
  3865. break;
  3866. default:
  3867. store.CopyChunkData( walk );
  3868. break;
  3869. }
  3870. store.ChunkFinish();
  3871. if ( skipchunk )
  3872. {
  3873. store.ChunkSetPosition( originalPos );
  3874. }
  3875. walk.ChunkNext();
  3876. }
  3877. if ( !wordtrackwritten )
  3878. {
  3879. store.ChunkStart( WAVE_VALVEDATA );
  3880. StoreValveDataChunk( store );
  3881. store.ChunkFinish();
  3882. }
  3883. }
  3884. //-----------------------------------------------------------------------------
  3885. // Purpose: Copy phoneme data in from wave file we sent for resprocessing
  3886. //-----------------------------------------------------------------------------
  3887. void PhonemeEditor::RetrieveLinguisticData( void )
  3888. {
  3889. if ( !m_pWaveFile )
  3890. return;
  3891. m_Tags.Reset();
  3892. ReadLinguisticTags();
  3893. redraw();
  3894. }
  3895. bool PhonemeEditor::StopPlayback( void )
  3896. {
  3897. bool bret = false;
  3898. if ( m_pWaveFile )
  3899. {
  3900. SetScrubTargetTime( m_flScrub );
  3901. if ( sound->IsSoundPlaying( m_pMixer ) )
  3902. {
  3903. sound->StopAll();
  3904. bret = true;
  3905. }
  3906. }
  3907. sound->Flush();
  3908. return bret;
  3909. }
  3910. CPhonemeTag *PhonemeEditor::GetPhonemeTagUnderMouse( int mx, int my )
  3911. {
  3912. if ( GetMode() != MODE_PHONEMES )
  3913. return NULL;
  3914. if ( !m_pWaveFile )
  3915. return NULL;
  3916. // FIXME: Don't read from file, read from arrays after LISET finishes
  3917. // Deterime if phoneme boundary is under the cursor
  3918. //
  3919. RECT rc;
  3920. GetWorkspaceRect( rc );
  3921. if ( !IsMouseOverPhonemeRow( my ) )
  3922. return NULL;
  3923. if ( GetPixelsPerSecond() <= 0 )
  3924. return NULL;
  3925. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  3926. float endtime = w2() / GetPixelsPerSecond() + starttime;
  3927. if ( endtime - starttime <= 0.0f )
  3928. return NULL;
  3929. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  3930. {
  3931. CWordTag *word = m_Tags.m_Words[ i ];
  3932. Assert( word );
  3933. if ( !word )
  3934. continue;
  3935. for ( int k = 0; k < word->m_Phonemes.Count(); k++ )
  3936. {
  3937. CPhonemeTag *pPhoneme = word->m_Phonemes[ k ];
  3938. Assert( pPhoneme );
  3939. if ( !pPhoneme )
  3940. continue;
  3941. float t1 = pPhoneme->GetStartTime();
  3942. float t2 = pPhoneme->GetEndTime();
  3943. float frac1 = ( t1 - starttime ) / ( endtime - starttime );
  3944. float frac2 = ( t2 - starttime ) / ( endtime - starttime );
  3945. frac1 = min( 1.0f, frac1 );
  3946. frac1 = max( 0.0f, frac1 );
  3947. frac2 = min( 1.0f, frac2 );
  3948. frac2 = max( 0.0f, frac2 );
  3949. if ( frac1 == frac2 )
  3950. continue;
  3951. int x1 = ( int )( frac1 * w2() );
  3952. int x2 = ( int )( frac2 * w2() );
  3953. if ( mx >= x1 && mx <= x2 )
  3954. {
  3955. return pPhoneme;
  3956. }
  3957. }
  3958. }
  3959. return NULL;
  3960. }
  3961. CWordTag *PhonemeEditor::GetWordTagUnderMouse( int mx, int my )
  3962. {
  3963. if ( GetMode() != MODE_PHONEMES )
  3964. return NULL;
  3965. // Deterime if phoneme boundary is under the cursor
  3966. //
  3967. if ( !m_pWaveFile )
  3968. return NULL;
  3969. RECT rc;
  3970. GetWorkspaceRect( rc );
  3971. if ( !IsMouseOverWordRow( my ) )
  3972. return NULL;
  3973. if ( GetPixelsPerSecond() <= 0 )
  3974. return NULL;
  3975. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  3976. float endtime = w2() / GetPixelsPerSecond() + starttime;
  3977. if ( endtime - starttime <= 0.0f )
  3978. return NULL;
  3979. for ( int k = 0; k < m_Tags.m_Words.Count(); k++ )
  3980. {
  3981. CWordTag *word = m_Tags.m_Words[ k ];
  3982. Assert( word );
  3983. if ( !word )
  3984. continue;
  3985. float t1 = word->m_flStartTime;
  3986. float t2 = word->m_flEndTime;
  3987. float frac1 = ( t1 - starttime ) / ( endtime - starttime );
  3988. float frac2 = ( t2 - starttime ) / ( endtime - starttime );
  3989. frac1 = min( 1.0f, frac1 );
  3990. frac1 = max( 0.0f, frac1 );
  3991. frac2 = min( 1.0f, frac2 );
  3992. frac2 = max( 0.0f, frac2 );
  3993. if ( frac1 == frac2 )
  3994. continue;
  3995. int x1 = ( int )( frac1 * w2() );
  3996. int x2 = ( int )( frac2 * w2() );
  3997. if ( mx >= x1 && mx <= x2 )
  3998. {
  3999. return word;
  4000. }
  4001. }
  4002. return NULL;
  4003. }
  4004. void PhonemeEditor::DeselectWords( void )
  4005. {
  4006. if ( GetMode() != MODE_PHONEMES )
  4007. return;
  4008. for ( int i = 0 ; i < m_Tags.m_Words.Count(); i++ )
  4009. {
  4010. CWordTag *w = m_Tags.m_Words[ i ];
  4011. Assert( w );
  4012. if ( !w )
  4013. continue;
  4014. w->m_bSelected = false;
  4015. }
  4016. }
  4017. void PhonemeEditor::DeselectPhonemes( void )
  4018. {
  4019. if ( GetMode() != MODE_PHONEMES )
  4020. return;
  4021. for ( int w = 0 ; w < m_Tags.m_Words.Count(); w++ )
  4022. {
  4023. CWordTag *word = m_Tags.m_Words[ w ];
  4024. Assert( word );
  4025. if ( !word )
  4026. continue;
  4027. for ( int i = 0 ; i < word->m_Phonemes.Count(); i++ )
  4028. {
  4029. CPhonemeTag *pt = word->m_Phonemes[ i ];
  4030. Assert( pt );
  4031. if ( !pt )
  4032. continue;
  4033. pt->m_bSelected = false;
  4034. }
  4035. }
  4036. }
  4037. void PhonemeEditor::SnapWords( void )
  4038. {
  4039. if ( GetMode() != MODE_PHONEMES )
  4040. return;
  4041. if ( m_Tags.m_Words.Count() < 2 )
  4042. {
  4043. Con_Printf( "Can't snap, need at least two contiguous selected words\n" );
  4044. return;
  4045. }
  4046. SetDirty( true );
  4047. PushUndo();
  4048. for ( int i = 0; i < m_Tags.m_Words.Count() - 1; i++ )
  4049. {
  4050. CWordTag *current = m_Tags.m_Words[ i ];
  4051. CWordTag *next = m_Tags.m_Words[ i + 1 ];
  4052. Assert( current && next );
  4053. if ( !current->m_bSelected || !next->m_bSelected )
  4054. continue;
  4055. // Move next word to end of current
  4056. next->m_flStartTime = current->m_flEndTime;
  4057. }
  4058. PushRedo();
  4059. redraw();
  4060. }
  4061. void PhonemeEditor::SeparateWords( void )
  4062. {
  4063. if ( GetMode() != MODE_PHONEMES )
  4064. return;
  4065. if ( GetPixelsPerSecond() <= 0.0f )
  4066. return;
  4067. if ( m_Tags.m_Words.Count() < 2 )
  4068. {
  4069. Con_Printf( "Can't separate, need at least two contiguous selected words\n" );
  4070. return;
  4071. }
  4072. // Three pixels
  4073. double time_epsilon = ( 1.0f / GetPixelsPerSecond() ) * 6;
  4074. SetDirty( true );
  4075. PushUndo();
  4076. for ( int i = 0; i < m_Tags.m_Words.Count() - 1; i++ )
  4077. {
  4078. CWordTag *current = m_Tags.m_Words[ i ];
  4079. CWordTag *next = m_Tags.m_Words[ i + 1 ];
  4080. Assert( current && next );
  4081. if ( !current->m_bSelected || !next->m_bSelected )
  4082. continue;
  4083. // Close enough?
  4084. if ( fabs( current->m_flEndTime - next->m_flStartTime ) > time_epsilon )
  4085. {
  4086. Con_Printf( "Can't split %s and %s, already split apart\n",
  4087. current->GetWord(), next->GetWord() );
  4088. continue;
  4089. }
  4090. // Offset next word start time a bit
  4091. next->m_flStartTime += time_epsilon;
  4092. break;
  4093. }
  4094. PushRedo();
  4095. redraw();
  4096. }
  4097. void PhonemeEditor::CreateEvenWordDistribution( const char *wordlist )
  4098. {
  4099. if ( GetMode() != MODE_PHONEMES )
  4100. return;
  4101. if( !m_pWaveFile )
  4102. return;
  4103. Assert( wordlist );
  4104. if ( !wordlist )
  4105. return;
  4106. m_Tags.CreateEventWordDistribution( wordlist, m_pWaveFile->GetRunningLength() );
  4107. redraw();
  4108. }
  4109. void PhonemeEditor::EditWordList( void )
  4110. {
  4111. if ( GetMode() != MODE_PHONEMES )
  4112. return;
  4113. if ( !m_pWaveFile )
  4114. return;
  4115. // Build word string
  4116. char wordstring[ 1024 ];
  4117. strcpy( wordstring, m_Tags.GetText() );
  4118. CInputParams params;
  4119. memset( &params, 0, sizeof( params ) );
  4120. strcpy( params.m_szDialogTitle, "Word List" );
  4121. strcpy( params.m_szPrompt, "Sentence:" );
  4122. strcpy( params.m_szInputText, wordstring );
  4123. if ( !InputProperties( &params ) )
  4124. return;
  4125. if ( strlen( params.m_szInputText ) <= 0 )
  4126. {
  4127. // Could be foreign language...
  4128. Warning( "Edit word list: No words entered!\n" );
  4129. }
  4130. SetDirty( true );
  4131. PushUndo();
  4132. // Clear any current LISET results
  4133. ClearExtracted();
  4134. // Force text
  4135. m_Tags.SetText( params.m_szInputText );
  4136. if ( m_Tags.m_Words.Count() == 0 )
  4137. {
  4138. // First text we've seen, just distribute words evenly
  4139. CreateEvenWordDistribution( params.m_szInputText );
  4140. // Redo liset
  4141. RedoPhonemeExtraction();
  4142. }
  4143. PushRedo();
  4144. SetFocus( (HWND)getHandle() );
  4145. redraw();
  4146. }
  4147. //-----------------------------------------------------------------------------
  4148. // Purpose: Overwrite original wave with changes
  4149. //-----------------------------------------------------------------------------
  4150. void PhonemeEditor::CommitChanges( void )
  4151. {
  4152. SaveLinguisticData();
  4153. // Make it writable - if possible
  4154. MakeFileWriteable( m_WorkFile.m_szWaveFile );
  4155. //Open a message box to warn the user if the file was unable to be made non-read only
  4156. if ( !IsFileWriteable( m_WorkFile.m_szWaveFile ) )
  4157. {
  4158. mxMessageBox( NULL, va( "Unable to save file '%s'. File is read-only or in use.",
  4159. m_WorkFile.m_szWaveFile ), g_appTitle, MX_MB_OK );
  4160. }
  4161. else
  4162. {
  4163. // Copy over and overwrite file
  4164. FPCopyFile( m_WorkFile.m_szWorkingFile, m_WorkFile.m_szWaveFile, true );
  4165. Msg( "Changes saved to '%s'\n", m_WorkFile.m_szWaveFile );
  4166. SetDirty( false, false );
  4167. }
  4168. }
  4169. //-----------------------------------------------------------------------------
  4170. // Purpose:
  4171. //-----------------------------------------------------------------------------
  4172. void PhonemeEditor::LoadWaveFile( void )
  4173. {
  4174. char filename[ 512 ];
  4175. if ( !FacePoser_ShowOpenFileNameDialog( filename, sizeof( filename ), "sound", "*.wav" ) )
  4176. {
  4177. return;
  4178. }
  4179. StopPlayback();
  4180. // Strip out the game directory
  4181. SetCurrentWaveFile( filename );
  4182. }
  4183. void PhonemeEditor::SnapPhonemes( void )
  4184. {
  4185. if ( GetMode() != MODE_PHONEMES )
  4186. return;
  4187. SetDirty( true );
  4188. PushUndo();
  4189. CPhonemeTag *prev = NULL;
  4190. for ( int w = 0; w < m_Tags.m_Words.Count(); w++ )
  4191. {
  4192. CWordTag *word = m_Tags.m_Words[ w ];
  4193. Assert( word );
  4194. if ( !word )
  4195. continue;
  4196. for ( int i = 0; i < word->m_Phonemes.Count(); i++ )
  4197. {
  4198. CPhonemeTag *current = word->m_Phonemes[ i ];
  4199. Assert( current );
  4200. if ( current->m_bSelected )
  4201. {
  4202. if (prev)
  4203. {
  4204. // More start of next to end of previous
  4205. prev->SetEndTime( current->GetStartTime() );
  4206. }
  4207. prev = current;
  4208. }
  4209. else
  4210. {
  4211. prev = NULL;
  4212. }
  4213. }
  4214. }
  4215. PushRedo();
  4216. redraw();
  4217. }
  4218. void PhonemeEditor::SeparatePhonemes( void )
  4219. {
  4220. if ( GetMode() != MODE_PHONEMES )
  4221. return;
  4222. SetDirty( true );
  4223. PushUndo();
  4224. // Three pixels
  4225. double time_epsilon = ( 1.0f / GetPixelsPerSecond() ) * 6;
  4226. CPhonemeTag *prev = NULL;
  4227. for ( int w = 0; w < m_Tags.m_Words.Count(); w++ )
  4228. {
  4229. CWordTag *word = m_Tags.m_Words[ w ];
  4230. Assert( word );
  4231. if ( !word )
  4232. continue;
  4233. for ( int i = 0; i < word->m_Phonemes.Count(); i++ )
  4234. {
  4235. CPhonemeTag *current = word->m_Phonemes[ i ];
  4236. Assert( current );
  4237. if ( current->m_bSelected )
  4238. {
  4239. if ( prev )
  4240. {
  4241. // Close enough?
  4242. if ( fabs( prev->GetEndTime() - current->GetStartTime() ) > time_epsilon )
  4243. {
  4244. Con_Printf( "Can't split already split apart\n" );
  4245. continue;
  4246. }
  4247. current->AddStartTime( time_epsilon );
  4248. }
  4249. prev = current;
  4250. }
  4251. else
  4252. {
  4253. prev = NULL;
  4254. }
  4255. }
  4256. }
  4257. PushRedo();
  4258. redraw();
  4259. }
  4260. bool PhonemeEditor::IsMouseOverWordRow( int my )
  4261. {
  4262. if ( GetMode() != MODE_PHONEMES )
  4263. return false;
  4264. RECT rc;
  4265. GetWordTrayTopBottom( rc );
  4266. if ( my < rc.top )
  4267. return false;
  4268. if ( my > rc.bottom )
  4269. return false;
  4270. return true;
  4271. }
  4272. bool PhonemeEditor::IsMouseOverPhonemeRow( int my )
  4273. {
  4274. if ( GetMode() != MODE_PHONEMES )
  4275. return false;
  4276. RECT rc;
  4277. GetPhonemeTrayTopBottom( rc );
  4278. if ( my < rc.top )
  4279. return false;
  4280. if ( my > rc.bottom )
  4281. return false;
  4282. return true;
  4283. }
  4284. void PhonemeEditor::GetPhonemeTrayTopBottom( RECT& rc )
  4285. {
  4286. RECT wkrc;
  4287. GetWorkspaceRect( wkrc );
  4288. rc.top = wkrc.bottom - 2 * m_nTickHeight;
  4289. rc.bottom = wkrc.bottom - m_nTickHeight;
  4290. }
  4291. void PhonemeEditor::GetWordTrayTopBottom( RECT& rc )
  4292. {
  4293. RECT wkrc;
  4294. GetWorkspaceRect( wkrc );
  4295. rc.top = wkrc.top;
  4296. rc.bottom = wkrc.top + m_nTickHeight;
  4297. }
  4298. int PhonemeEditor::GetMouseForTime( float time )
  4299. {
  4300. RECT rc;
  4301. GetWorkspaceRect( rc );
  4302. if ( GetPixelsPerSecond() < 0.0f )
  4303. return rc.left;
  4304. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  4305. float endtime = w2() / GetPixelsPerSecond() + starttime;
  4306. if ( endtime - starttime <= 0.0f )
  4307. return rc.left;
  4308. float frac;
  4309. frac = ( time - starttime ) / ( endtime - starttime );
  4310. return rc.left + ( int )( rc.right * frac );
  4311. }
  4312. void PhonemeEditor::GetWordRect( const CWordTag *tag, RECT& rc )
  4313. {
  4314. Assert( tag );
  4315. GetWordTrayTopBottom( rc );
  4316. rc.left = GetMouseForTime( tag->m_flStartTime );
  4317. rc.right = GetMouseForTime( tag->m_flEndTime );
  4318. }
  4319. void PhonemeEditor::GetPhonemeRect( const CPhonemeTag *tag, RECT& rc )
  4320. {
  4321. Assert( tag );
  4322. GetPhonemeTrayTopBottom( rc );
  4323. rc.left = GetMouseForTime( tag->GetStartTime() );
  4324. rc.right = GetMouseForTime( tag->GetEndTime() );
  4325. }
  4326. //-----------------------------------------------------------------------------
  4327. // Purpose:
  4328. //-----------------------------------------------------------------------------
  4329. void PhonemeEditor::CommitExtracted( void )
  4330. {
  4331. if ( GetMode() != MODE_PHONEMES )
  4332. return;
  4333. m_nLastExtractionResult = SR_RESULT_NORESULT;
  4334. if ( !m_TagsExt.m_Words.Count() )
  4335. return;
  4336. SetDirty( true );
  4337. PushUndo();
  4338. m_Tags.Reset();
  4339. m_Tags = m_TagsExt;
  4340. PushRedo();
  4341. ClearExtracted();
  4342. redraw();
  4343. }
  4344. //-----------------------------------------------------------------------------
  4345. // Purpose:
  4346. //-----------------------------------------------------------------------------
  4347. void PhonemeEditor::ClearExtracted( void )
  4348. {
  4349. if ( GetMode() != MODE_PHONEMES )
  4350. return;
  4351. m_nLastExtractionResult = SR_RESULT_NORESULT;
  4352. m_TagsExt.Reset();
  4353. redraw();
  4354. }
  4355. //-----------------------------------------------------------------------------
  4356. // Purpose:
  4357. // Input : resultCode -
  4358. // Output : const char
  4359. //-----------------------------------------------------------------------------
  4360. const char *PhonemeEditor::GetExtractionResultString( int resultCode )
  4361. {
  4362. switch ( resultCode )
  4363. {
  4364. case SR_RESULT_NORESULT:
  4365. return "no extraction info.";
  4366. case SR_RESULT_ERROR:
  4367. return "an error occurred during extraction.";
  4368. case SR_RESULT_SUCCESS:
  4369. return "successful.";
  4370. case SR_RESULT_FAILED:
  4371. return "results retrieved, but full recognition failed.";
  4372. default:
  4373. break;
  4374. }
  4375. return "unknown result code.";
  4376. }
  4377. //-----------------------------------------------------------------------------
  4378. // Purpose:
  4379. // Input : mx -
  4380. // Output : CEventRelativeTag
  4381. //-----------------------------------------------------------------------------
  4382. CEventRelativeTag *PhonemeEditor::GetTagUnderMouse( int mx )
  4383. {
  4384. if ( GetMode() != MODE_PHONEMES )
  4385. return NULL;
  4386. // Figure out tag positions
  4387. if ( !m_pEvent || !m_pWaveFile )
  4388. return NULL;
  4389. RECT rc;
  4390. GetWorkspaceRect( rc );
  4391. RECT rcTags = rc;
  4392. if ( GetPixelsPerSecond() <= 0.0f )
  4393. return NULL;
  4394. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  4395. float endtime = w2() / GetPixelsPerSecond() + starttime;
  4396. if ( endtime - starttime < 0 )
  4397. return NULL;
  4398. for ( int i = 0; i < m_pEvent->GetNumRelativeTags(); i++ )
  4399. {
  4400. CEventRelativeTag *tag = m_pEvent->GetRelativeTag( i );
  4401. if ( !tag )
  4402. continue;
  4403. //
  4404. float tagtime = tag->GetPercentage() * m_pWaveFile->GetRunningLength();
  4405. if ( tagtime < starttime || tagtime > endtime )
  4406. continue;
  4407. float frac = ( tagtime - starttime ) / ( endtime - starttime );
  4408. int left = rcTags.left + (int)( frac * ( float )( rcTags.right - rcTags.left ) + 0.5f );
  4409. if ( abs( mx - left ) < 10 )
  4410. return tag;
  4411. }
  4412. return NULL;
  4413. }
  4414. //-----------------------------------------------------------------------------
  4415. // Purpose:
  4416. // Input : mx -
  4417. // my -
  4418. // Output : Returns true on success, false on failure.
  4419. //-----------------------------------------------------------------------------
  4420. bool PhonemeEditor::IsMouseOverTag( int mx, int my )
  4421. {
  4422. if ( GetMode() != MODE_PHONEMES )
  4423. return false;
  4424. if ( !IsMouseOverTagRow( my ) )
  4425. return false;
  4426. CEventRelativeTag *tag = GetTagUnderMouse( mx );
  4427. if ( !tag )
  4428. return false;
  4429. return true;
  4430. }
  4431. //-----------------------------------------------------------------------------
  4432. // Purpose:
  4433. // Input : startx -
  4434. // endx -
  4435. //-----------------------------------------------------------------------------
  4436. void PhonemeEditor::FinishEventTagDrag( int startx, int endx )
  4437. {
  4438. if ( !m_pWaveFile )
  4439. return;
  4440. if ( !m_pWaveFile->GetRunningLength() )
  4441. return;
  4442. // Find starting tag
  4443. CEventRelativeTag *tag = GetTagUnderMouse( startx );
  4444. if ( !tag )
  4445. return;
  4446. if ( GetPixelsPerSecond() <= 0 )
  4447. return;
  4448. // Convert mouse position to time
  4449. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  4450. float clicktime = (float)endx / GetPixelsPerSecond() + starttime;
  4451. float percent = clicktime / m_pWaveFile->GetRunningLength();
  4452. percent = clamp( percent, 0.0f, 1.0f );
  4453. tag->SetPercentage( percent );
  4454. redraw();
  4455. if ( g_pChoreoView )
  4456. {
  4457. g_pChoreoView->InvalidateLayout();
  4458. }
  4459. }
  4460. //-----------------------------------------------------------------------------
  4461. // Purpose:
  4462. // Input : my -
  4463. // Output : Returns true on success, false on failure.
  4464. //-----------------------------------------------------------------------------
  4465. bool PhonemeEditor::IsMouseOverTagRow( int my )
  4466. {
  4467. if ( GetMode() != MODE_PHONEMES )
  4468. return false;
  4469. if ( my < TAG_TOP || my > TAG_BOTTOM )
  4470. return false;
  4471. return true;
  4472. }
  4473. //-----------------------------------------------------------------------------
  4474. // Purpose:
  4475. // Input : mx -
  4476. // my -
  4477. //-----------------------------------------------------------------------------
  4478. void PhonemeEditor::ShowTagMenu( int mx, int my )
  4479. {
  4480. if ( GetMode() != MODE_PHONEMES )
  4481. return;
  4482. // Figure out tag positions
  4483. if ( !m_pEvent || !m_pWaveFile )
  4484. return;
  4485. if ( !IsMouseOverTagRow( my ) )
  4486. return;
  4487. CEventRelativeTag *tag = GetTagUnderMouse( mx );
  4488. mxPopupMenu *pop = new mxPopupMenu();
  4489. if ( tag )
  4490. {
  4491. pop->add( va( "Delete tag '%s'", tag->GetName() ), IDC_DELETETAG );
  4492. }
  4493. else
  4494. {
  4495. pop->add( va( "Add tag..." ), IDC_ADDTAG );
  4496. }
  4497. m_nClickX = mx;
  4498. pop->popup( this, mx, my );
  4499. }
  4500. //-----------------------------------------------------------------------------
  4501. // Purpose:
  4502. //-----------------------------------------------------------------------------
  4503. void PhonemeEditor::DeleteTag( void )
  4504. {
  4505. if ( GetMode() != MODE_PHONEMES )
  4506. return;
  4507. // Figure out tag positions
  4508. if ( !m_pEvent )
  4509. return;
  4510. CEventRelativeTag *tag = GetTagUnderMouse( m_nClickX );
  4511. if ( !tag )
  4512. return;
  4513. // Remove it
  4514. m_pEvent->RemoveRelativeTag( tag->GetName() );
  4515. g_pChoreoView->InvalidateLayout();
  4516. redraw();
  4517. }
  4518. //-----------------------------------------------------------------------------
  4519. // Purpose:
  4520. //-----------------------------------------------------------------------------
  4521. void PhonemeEditor::AddTag( void )
  4522. {
  4523. if ( GetMode() != MODE_PHONEMES )
  4524. return;
  4525. // Figure out tag positions
  4526. if ( !m_pEvent || !m_pWaveFile )
  4527. return;
  4528. CInputParams params;
  4529. memset( &params, 0, sizeof( params ) );
  4530. strcpy( params.m_szDialogTitle, "Event Tag Name" );
  4531. strcpy( params.m_szPrompt, "Name:" );
  4532. strcpy( params.m_szInputText, "" );
  4533. if ( !InputProperties( &params ) )
  4534. return;
  4535. if ( strlen( params.m_szInputText ) <= 0 )
  4536. {
  4537. Con_ErrorPrintf( "Event Tag Name: No name entered!\n" );
  4538. return;
  4539. }
  4540. // Convert mouse position to time
  4541. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  4542. float clicktime = (float)m_nClickX / GetPixelsPerSecond() + starttime;
  4543. float percent = clicktime / m_pWaveFile->GetRunningLength();
  4544. percent = min( 1.0f, percent );
  4545. percent = max( 0.0f, percent );
  4546. m_pEvent->AddRelativeTag( params.m_szInputText, percent );
  4547. g_pChoreoView->InvalidateLayout();
  4548. SetFocus( (HWND)getHandle() );
  4549. redraw();
  4550. }
  4551. //-----------------------------------------------------------------------------
  4552. // Purpose:
  4553. //-----------------------------------------------------------------------------
  4554. void PhonemeEditor::ClearEvent( void )
  4555. {
  4556. m_pEvent = NULL;
  4557. redraw();
  4558. }
  4559. void PhonemeEditor::TraverseWords( PEWORDITERFUNC pfn, float fparam )
  4560. {
  4561. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  4562. {
  4563. CWordTag *word = m_Tags.m_Words[ i ];
  4564. if ( !word )
  4565. continue;
  4566. (this->*pfn)( word, fparam );
  4567. }
  4568. }
  4569. void PhonemeEditor::TraversePhonemes( PEPHONEMEITERFUNC pfn, float fparam )
  4570. {
  4571. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  4572. {
  4573. CWordTag *word = m_Tags.m_Words[ i ];
  4574. if ( !word )
  4575. continue;
  4576. for ( int j = 0; j < word->m_Phonemes.Count(); j++ )
  4577. {
  4578. CPhonemeTag *phoneme = word->m_Phonemes[ j ];
  4579. if ( !phoneme )
  4580. continue;
  4581. (this->*pfn)( phoneme, word, fparam );
  4582. }
  4583. }
  4584. }
  4585. //-----------------------------------------------------------------------------
  4586. // Purpose:
  4587. // Input : amount -
  4588. //-----------------------------------------------------------------------------
  4589. void PhonemeEditor::ITER_MoveSelectedWords( CWordTag *word, float amount )
  4590. {
  4591. if ( !word->m_bSelected )
  4592. return;
  4593. word->m_flStartTime += amount;
  4594. word->m_flEndTime += amount;
  4595. }
  4596. void PhonemeEditor::ITER_MoveSelectedPhonemes( CPhonemeTag *phoneme, CWordTag *word, float amount )
  4597. {
  4598. if ( !phoneme->m_bSelected )
  4599. return;
  4600. phoneme->AddStartTime( amount );
  4601. phoneme->AddEndTime( amount );
  4602. }
  4603. void PhonemeEditor::ITER_ExtendSelectedPhonemeEndTimes( CPhonemeTag *phoneme, CWordTag *word, float amount )
  4604. {
  4605. if ( !phoneme->m_bSelected )
  4606. return;
  4607. if ( phoneme->GetEndTime() + amount <= phoneme->GetStartTime() )
  4608. return;
  4609. phoneme->AddEndTime( amount );
  4610. // Fixme, check for extending into next phoneme
  4611. }
  4612. void PhonemeEditor::ITER_ExtendSelectedWordEndTimes( CWordTag *word, float amount )
  4613. {
  4614. if ( !word->m_bSelected )
  4615. return;
  4616. if ( word->m_flEndTime + amount <= word->m_flStartTime )
  4617. return;
  4618. word->m_flEndTime += amount;
  4619. // Fixme, check for extending into next word
  4620. }
  4621. void PhonemeEditor::ITER_AddFocusRectSelectedWords( CWordTag *word, float amount )
  4622. {
  4623. if ( !word->m_bSelected )
  4624. return;
  4625. RECT wordRect;
  4626. GetWordRect( word, wordRect );
  4627. AddFocusRect( wordRect );
  4628. }
  4629. void PhonemeEditor::ITER_AddFocusRectSelectedPhonemes( CPhonemeTag *phoneme, CWordTag *word, float amount )
  4630. {
  4631. if ( !phoneme->m_bSelected )
  4632. return;
  4633. RECT phonemeRect;
  4634. GetPhonemeRect( phoneme, phonemeRect );
  4635. AddFocusRect( phonemeRect );
  4636. }
  4637. void PhonemeEditor::AddFocusRect( RECT& rc )
  4638. {
  4639. RECT rcFocus = rc;
  4640. POINT offset;
  4641. offset.x = 0;
  4642. offset.y = 0;
  4643. ClientToScreen( (HWND)getHandle(), &offset );
  4644. OffsetRect( &rcFocus, offset.x, offset.y );
  4645. // Convert to screen space?
  4646. CFocusRect fr;
  4647. fr.m_rcFocus = rcFocus;
  4648. fr.m_rcOrig = rcFocus;
  4649. m_FocusRects.AddToTail( fr );
  4650. }
  4651. void PhonemeEditor::CountSelected( void )
  4652. {
  4653. m_nSelectedPhonemeCount = 0;
  4654. m_nSelectedWordCount = 0;
  4655. TraverseWords( &PhonemeEditor::ITER_CountSelectedWords, 0.0f );
  4656. TraversePhonemes( &PhonemeEditor::ITER_CountSelectedPhonemes, 0.0f );
  4657. }
  4658. void PhonemeEditor::ITER_CountSelectedWords( CWordTag *word, float amount )
  4659. {
  4660. if ( !word->m_bSelected )
  4661. return;
  4662. m_nSelectedWordCount++;
  4663. }
  4664. void PhonemeEditor::ITER_CountSelectedPhonemes( CPhonemeTag *phoneme, CWordTag *word, float amount )
  4665. {
  4666. if ( !phoneme->m_bSelected )
  4667. return;
  4668. m_nSelectedPhonemeCount++;
  4669. }
  4670. // Undo/Redo
  4671. void PhonemeEditor::Undo( void )
  4672. {
  4673. if ( m_UndoStack.Count() > 0 && m_nUndoLevel > 0 )
  4674. {
  4675. m_nUndoLevel--;
  4676. PEUndo *u = m_UndoStack[ m_nUndoLevel ];
  4677. Assert( u->undo );
  4678. m_Tags = *(u->undo);
  4679. SetClickedPhoneme( -1, -1 );
  4680. }
  4681. redraw();
  4682. }
  4683. void PhonemeEditor::Redo( void )
  4684. {
  4685. if ( m_UndoStack.Count() > 0 && m_nUndoLevel <= m_UndoStack.Count() - 1 )
  4686. {
  4687. PEUndo *u = m_UndoStack[ m_nUndoLevel ];
  4688. Assert( u->redo );
  4689. m_Tags = *(u->redo);
  4690. m_nUndoLevel++;
  4691. SetClickedPhoneme( -1, -1 );
  4692. }
  4693. redraw();
  4694. }
  4695. void PhonemeEditor::PushUndo( void )
  4696. {
  4697. Assert( !m_bRedoPending );
  4698. m_bRedoPending = true;
  4699. WipeRedo();
  4700. // Copy current data
  4701. CSentence *u = new CSentence();
  4702. *u = m_Tags;
  4703. PEUndo *undo = new PEUndo;
  4704. undo->undo = u;
  4705. undo->redo = NULL;
  4706. m_UndoStack.AddToTail( undo );
  4707. m_nUndoLevel++;
  4708. }
  4709. void PhonemeEditor::PushRedo( void )
  4710. {
  4711. Assert( m_bRedoPending );
  4712. m_bRedoPending = false;
  4713. // Copy current data
  4714. CSentence *r = new CSentence();
  4715. *r = m_Tags;
  4716. PEUndo *undo = m_UndoStack[ m_nUndoLevel - 1 ];
  4717. undo->redo = r;
  4718. }
  4719. void PhonemeEditor::WipeUndo( void )
  4720. {
  4721. while ( m_UndoStack.Count() > 0 )
  4722. {
  4723. PEUndo *u = m_UndoStack[ 0 ];
  4724. delete u->undo;
  4725. delete u->redo;
  4726. delete u;
  4727. m_UndoStack.Remove( 0 );
  4728. }
  4729. m_nUndoLevel = 0;
  4730. }
  4731. void PhonemeEditor::WipeRedo( void )
  4732. {
  4733. // Wipe everything above level
  4734. while ( m_UndoStack.Count() > m_nUndoLevel )
  4735. {
  4736. PEUndo *u = m_UndoStack[ m_nUndoLevel ];
  4737. delete u->undo;
  4738. delete u->redo;
  4739. delete u;
  4740. m_UndoStack.Remove( m_nUndoLevel );
  4741. }
  4742. }
  4743. //-----------------------------------------------------------------------------
  4744. // Purpose:
  4745. // Input : word -
  4746. // phoneme -
  4747. //-----------------------------------------------------------------------------
  4748. void PhonemeEditor::SetClickedPhoneme( int word, int phoneme )
  4749. {
  4750. m_nClickedPhoneme = phoneme;
  4751. m_nClickedWord = word;
  4752. }
  4753. //-----------------------------------------------------------------------------
  4754. // Purpose:
  4755. // Output : CPhonemeTag
  4756. //-----------------------------------------------------------------------------
  4757. CPhonemeTag *PhonemeEditor::GetClickedPhoneme( void )
  4758. {
  4759. if ( m_nClickedPhoneme < 0 || m_nClickedWord < 0 )
  4760. return NULL;
  4761. if ( m_nClickedWord >= m_Tags.m_Words.Count() )
  4762. return NULL;
  4763. CWordTag *word = m_Tags.m_Words[ m_nClickedWord ];
  4764. if ( !word )
  4765. return NULL;
  4766. if ( m_nClickedPhoneme >= word->m_Phonemes.Count() )
  4767. return NULL;
  4768. CPhonemeTag *phoneme = word->m_Phonemes[ m_nClickedPhoneme ];
  4769. return phoneme;
  4770. }
  4771. //-----------------------------------------------------------------------------
  4772. // Purpose:
  4773. // Output : CWordTag
  4774. //-----------------------------------------------------------------------------
  4775. CWordTag *PhonemeEditor::GetClickedWord( void )
  4776. {
  4777. if ( m_nClickedWord < 0 )
  4778. return NULL;
  4779. if ( m_nClickedWord >= m_Tags.m_Words.Count() )
  4780. return NULL;
  4781. CWordTag *word = m_Tags.m_Words[ m_nClickedWord ];
  4782. return word;
  4783. }
  4784. void PhonemeEditor::ShowContextMenu_Phonemes( int mx, int my )
  4785. {
  4786. CountSelected();
  4787. // Construct main
  4788. mxPopupMenu *pop = new mxPopupMenu();
  4789. if ( m_pWaveFile )
  4790. {
  4791. mxPopupMenu *play = new mxPopupMenu;
  4792. play->add( va( "Original" ), IDC_PHONEME_PLAY_ORIG );
  4793. play->add( va( "Edited" ), IDC_PLAY_EDITED );
  4794. if ( m_bSelectionActive )
  4795. {
  4796. play->add( va( "Selection" ), IDC_PLAY_EDITED_SELECTION );
  4797. }
  4798. pop->addMenu( "Play", play );
  4799. if ( sound->IsSoundPlaying( m_pMixer ) )
  4800. {
  4801. pop->add( va( "Cancel playback" ), IDC_CANCELPLAYBACK );
  4802. }
  4803. pop->addSeparator();
  4804. }
  4805. pop->add( va( "Load..." ), IDC_LOADWAVEFILE );
  4806. if ( m_pWaveFile )
  4807. {
  4808. pop->add( va( "Save" ), IDC_SAVE_LINGUISTIC );
  4809. }
  4810. if ( m_bSelectionActive )
  4811. {
  4812. pop->addSeparator();
  4813. pop->add( va( "Deselect" ), IDC_DESELECT );
  4814. }
  4815. if ( m_pWaveFile )
  4816. {
  4817. pop->addSeparator();
  4818. pop->add( va( "Redo Extraction" ), IDC_REDO_PHONEMEEXTRACTION );
  4819. if ( m_nSelectedWordCount < 1 || AreSelectedWordsContiguous() )
  4820. {
  4821. pop->add( va( "Redo Extraction of selected words" ), IDC_REDO_PHONEMEEXTRACTION_SELECTION );
  4822. }
  4823. }
  4824. if ( m_pWaveFile && m_TagsExt.m_Words.Count() )
  4825. {
  4826. pop->addSeparator();
  4827. pop->add( va( "Commit extraction" ) , IDC_COMMITEXTRACTED );
  4828. pop->add( va( "Clear extraction" ), IDC_CLEAREXTRACTED );
  4829. }
  4830. if ( m_nUndoLevel != 0 || m_nUndoLevel != m_UndoStack.Count() )
  4831. {
  4832. pop->addSeparator();
  4833. if ( m_nUndoLevel != 0 )
  4834. {
  4835. pop->add( va( "Undo" ), IDC_UNDO );
  4836. }
  4837. if ( m_nUndoLevel != m_UndoStack.Count() )
  4838. {
  4839. pop->add( va( "Redo" ), IDC_REDO );
  4840. }
  4841. pop->add( va( "Clear Undo Info" ), IDC_CLEARUNDO );
  4842. }
  4843. if ( m_Tags.m_Words.Count() > 0 )
  4844. {
  4845. pop->addSeparator();
  4846. pop->add( va( "Cleanup words/phonemes" ), IDC_CLEANUP );
  4847. }
  4848. // Show hierarchical options menu
  4849. {
  4850. mxPopupMenu *api = 0;
  4851. if ( DoesExtractorExistFor( SPEECH_API_SAPI ) )
  4852. {
  4853. api = new mxPopupMenu();
  4854. api->add( "Microsoft Speech API", IDC_API_SAPI );
  4855. if ( g_viewerSettings.speechapiindex == SPEECH_API_SAPI )
  4856. {
  4857. api->setChecked( IDC_API_SAPI, true );
  4858. }
  4859. }
  4860. if ( DoesExtractorExistFor( SPEECH_API_LIPSINC ) )
  4861. {
  4862. if ( !api )
  4863. api = new mxPopupMenu();
  4864. api->add( "Lipsinc Speech API", IDC_API_LIPSINC );
  4865. if ( g_viewerSettings.speechapiindex == SPEECH_API_LIPSINC )
  4866. {
  4867. api->setChecked( IDC_API_LIPSINC, true );
  4868. }
  4869. }
  4870. pop->addSeparator();
  4871. pop->addMenu( "Change Speech API", api );
  4872. }
  4873. // Import export menu
  4874. if ( m_pWaveFile )
  4875. {
  4876. pop->addSeparator();
  4877. if ( m_Tags.m_Words.Count() > 0 )
  4878. {
  4879. pop->add( "Export word data to " WORD_DATA_EXTENSION "...", IDC_EXPORT_SENTENCE );
  4880. }
  4881. pop->add( "Import word data from " WORD_DATA_EXTENSION "...", IDC_IMPORT_SENTENCE );
  4882. pop->add( va("%s Voice Duck", m_Tags.GetVoiceDuck() ? "Disable" : "Enable" ), IDC_TOGGLE_VOICEDUCK );
  4883. }
  4884. pop->popup( this, mx, my );
  4885. }
  4886. void PhonemeEditor::ShowContextMenu_Emphasis( int mx, int my )
  4887. {
  4888. Emphasis_CountSelected();
  4889. // Construct main
  4890. mxPopupMenu *pop = new mxPopupMenu();
  4891. pop->add( va( "Select All" ), IDC_EMPHASIS_SELECTALL );
  4892. if ( m_nNumSelected > 0 )
  4893. {
  4894. pop->add( va( "Deselect All" ), IDC_EMPHASIS_DESELECT );
  4895. }
  4896. if ( m_nUndoLevel != 0 || m_nUndoLevel != m_UndoStack.Count() )
  4897. {
  4898. pop->addSeparator();
  4899. if ( m_nUndoLevel != 0 )
  4900. {
  4901. pop->add( va( "Undo" ), IDC_UNDO );
  4902. }
  4903. if ( m_nUndoLevel != m_UndoStack.Count() )
  4904. {
  4905. pop->add( va( "Redo" ), IDC_REDO );
  4906. }
  4907. pop->add( va( "Clear Undo Info" ), IDC_CLEARUNDO );
  4908. }
  4909. pop->popup( this, mx, my );
  4910. }
  4911. void PhonemeEditor::ShowContextMenu( int mx, int my )
  4912. {
  4913. switch ( GetMode() )
  4914. {
  4915. default:
  4916. case MODE_PHONEMES:
  4917. ShowContextMenu_Phonemes( mx, my );
  4918. break;
  4919. case MODE_EMPHASIS:
  4920. ShowContextMenu_Emphasis( mx, my );
  4921. break;
  4922. }
  4923. }
  4924. void PhonemeEditor::ShiftSelectedPhoneme( int direction )
  4925. {
  4926. if ( GetMode() != MODE_PHONEMES )
  4927. return;
  4928. CountSelected();
  4929. switch ( m_nSelectedPhonemeCount )
  4930. {
  4931. case 1:
  4932. break;
  4933. case 0:
  4934. Con_Printf( "Can't shift phonemes, none selected\n" );
  4935. return;
  4936. default:
  4937. Con_Printf( "Can only shift one phoneme at a time via keyboard\n" );
  4938. return;
  4939. }
  4940. RECT rc;
  4941. GetWorkspaceRect( rc );
  4942. // Determine start/stop positions
  4943. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  4944. float endtime = w2() / GetPixelsPerSecond() + starttime;
  4945. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  4946. float movetime = timeperpixel * (float)direction;
  4947. float maxmove = ComputeMaxPhonemeShift( direction > 0 ? true : false, false );
  4948. if ( direction > 0 )
  4949. {
  4950. if ( movetime > maxmove )
  4951. {
  4952. movetime = maxmove;
  4953. Con_Printf( "Further shift is blocked on right\n" );
  4954. }
  4955. }
  4956. else
  4957. {
  4958. if ( movetime < -maxmove )
  4959. {
  4960. movetime = -maxmove;
  4961. Con_Printf( "Further shift is blocked on left\n" );
  4962. }
  4963. }
  4964. if ( fabs( movetime ) < 0.0001f )
  4965. return;
  4966. SetDirty( true );
  4967. PushUndo();
  4968. TraversePhonemes( &PhonemeEditor::ITER_MoveSelectedPhonemes, movetime );
  4969. PushRedo();
  4970. m_bWordsActive = false;
  4971. redraw();
  4972. Con_Printf( "Shift phoneme %s\n", direction == -1 ? "left" : "right" );
  4973. }
  4974. void PhonemeEditor::ExtendSelectedPhonemeEndTime( int direction )
  4975. {
  4976. if ( GetMode() != MODE_PHONEMES )
  4977. return;
  4978. CountSelected();
  4979. if ( m_nSelectedPhonemeCount != 1 )
  4980. return;
  4981. RECT rc;
  4982. GetWorkspaceRect( rc );
  4983. // Determine start/stop positions
  4984. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  4985. float endtime = w2() / GetPixelsPerSecond() + starttime;
  4986. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  4987. float movetime = timeperpixel * (float)direction;
  4988. SetDirty( true );
  4989. PushUndo();
  4990. TraversePhonemes( &PhonemeEditor::ITER_ExtendSelectedPhonemeEndTimes, movetime );
  4991. PushRedo();
  4992. m_bWordsActive = false;
  4993. redraw();
  4994. Con_Printf( "Extend phoneme end %s\n", direction == -1 ? "left" : "right" );
  4995. }
  4996. void PhonemeEditor::SelectNextPhoneme( int direction )
  4997. {
  4998. if ( GetMode() != MODE_PHONEMES )
  4999. return;
  5000. CountSelected();
  5001. if ( m_nSelectedPhonemeCount != 1 )
  5002. {
  5003. if ( m_nSelectedWordCount == 1 )
  5004. {
  5005. CWordTag *word = GetSelectedWord();
  5006. if ( word && word->m_Phonemes.Count() > 0 )
  5007. {
  5008. m_nSelectedPhonemeCount = 1;
  5009. CPhonemeTag *p = word->m_Phonemes[ direction ? word->m_Phonemes.Count() - 1 : 0 ];
  5010. p->m_bSelected = true;
  5011. }
  5012. else
  5013. {
  5014. return;
  5015. }
  5016. }
  5017. else
  5018. {
  5019. return;
  5020. }
  5021. }
  5022. Con_Printf( "Move to next phoneme %s\n", direction == -1 ? "left" : "right" );
  5023. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  5024. {
  5025. CWordTag *word = m_Tags.m_Words[ i ];
  5026. if ( !word )
  5027. continue;
  5028. for ( int j = 0; j < word->m_Phonemes.Count(); j++ )
  5029. {
  5030. CPhonemeTag *phoneme = word->m_Phonemes[ j ];
  5031. if ( !phoneme )
  5032. continue;
  5033. if ( !phoneme->m_bSelected )
  5034. continue;
  5035. // Deselect this one and move
  5036. int nextindex = j + direction;
  5037. if ( nextindex < 0 )
  5038. {
  5039. nextindex = word->m_Phonemes.Count() - 1;
  5040. }
  5041. else if ( nextindex >= word->m_Phonemes.Count() )
  5042. {
  5043. nextindex = 0;
  5044. }
  5045. phoneme->m_bSelected = false;
  5046. phoneme = word->m_Phonemes[ nextindex ];
  5047. phoneme->m_bSelected = true;
  5048. m_bWordsActive = false;
  5049. redraw();
  5050. return;
  5051. }
  5052. }
  5053. }
  5054. bool PhonemeEditor::IsPhonemeSelected( CWordTag *word )
  5055. {
  5056. for ( int i = 0 ; i < word->m_Phonemes.Count(); i++ )
  5057. {
  5058. CPhonemeTag *p = word->m_Phonemes[ i ];
  5059. if ( !p || !p->m_bSelected )
  5060. continue;
  5061. return true;
  5062. }
  5063. return false;
  5064. }
  5065. void PhonemeEditor::SelectNextWord( int direction )
  5066. {
  5067. if ( GetMode() != MODE_PHONEMES )
  5068. return;
  5069. CountSelected();
  5070. if ( m_nSelectedWordCount != 1 &&
  5071. m_nSelectedPhonemeCount != 1 )
  5072. {
  5073. // Selected first word then
  5074. if ( m_nSelectedWordCount == 0 && m_Tags.m_Words.Count() > 0 )
  5075. {
  5076. CWordTag *word = m_Tags.m_Words[ direction ? m_Tags.m_Words.Count() - 1 : 0 ];
  5077. word->m_bSelected = true;
  5078. m_nSelectedWordCount = 1;
  5079. }
  5080. else
  5081. {
  5082. return;
  5083. }
  5084. }
  5085. Con_Printf( "Move to next word %s\n", direction == -1 ? "left" : "right" );
  5086. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  5087. {
  5088. CWordTag *word = m_Tags.m_Words[ i ];
  5089. if ( !word )
  5090. continue;
  5091. if ( m_nSelectedWordCount == 1 )
  5092. {
  5093. if ( !word->m_bSelected )
  5094. continue;
  5095. }
  5096. else
  5097. {
  5098. if ( !IsPhonemeSelected( word ) )
  5099. continue;
  5100. }
  5101. // Deselect word
  5102. word->m_bSelected = false;
  5103. for ( int j = 0; j < word->m_Phonemes.Count(); j++ )
  5104. {
  5105. CPhonemeTag *phoneme = word->m_Phonemes[ j ];
  5106. if ( !phoneme )
  5107. continue;
  5108. if ( !phoneme->m_bSelected )
  5109. continue;
  5110. phoneme->m_bSelected = false;
  5111. }
  5112. // Deselect this one and move
  5113. int nextword = i + direction;
  5114. if ( nextword < 0 )
  5115. {
  5116. nextword = m_Tags.m_Words.Count() - 1;
  5117. }
  5118. else if ( nextword >= m_Tags.m_Words.Count() )
  5119. {
  5120. nextword = 0;
  5121. }
  5122. word = m_Tags.m_Words[ nextword ];
  5123. word->m_bSelected = true;
  5124. if ( word->m_Phonemes.Count() > 0 )
  5125. {
  5126. CPhonemeTag *phoneme = NULL;
  5127. if ( direction > 0 )
  5128. {
  5129. phoneme = word->m_Phonemes[ 0 ];
  5130. }
  5131. else
  5132. {
  5133. phoneme = word->m_Phonemes[ word->m_Phonemes.Count() - 1 ];
  5134. }
  5135. phoneme->m_bSelected = true;
  5136. }
  5137. m_bWordsActive = true;
  5138. redraw();
  5139. return;
  5140. }
  5141. }
  5142. void PhonemeEditor::ShiftSelectedWord( int direction )
  5143. {
  5144. if ( GetMode() != MODE_PHONEMES )
  5145. return;
  5146. CountSelected();
  5147. switch ( m_nSelectedWordCount )
  5148. {
  5149. case 1:
  5150. break;
  5151. case 0:
  5152. Con_Printf( "Can't shift words, none selected\n" );
  5153. return;
  5154. default:
  5155. Con_Printf( "Can only shift one word at a time via keyboard\n" );
  5156. return;
  5157. }
  5158. RECT rc;
  5159. GetWorkspaceRect( rc );
  5160. // Determine start/stop positions
  5161. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  5162. float endtime = w2() / GetPixelsPerSecond() + starttime;
  5163. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  5164. float movetime = timeperpixel * (float)direction;
  5165. float maxmove = ComputeMaxWordShift( direction > 0 ? true : false, false );
  5166. if ( direction > 0 )
  5167. {
  5168. if ( movetime > maxmove )
  5169. {
  5170. movetime = maxmove;
  5171. Con_Printf( "Further shift is blocked on right\n" );
  5172. }
  5173. }
  5174. else
  5175. {
  5176. if ( movetime < -maxmove )
  5177. {
  5178. movetime = -maxmove;
  5179. Con_Printf( "Further shift is blocked on left\n" );
  5180. }
  5181. }
  5182. if ( fabs( movetime ) < 0.0001f )
  5183. return;
  5184. SetDirty( true );
  5185. PushUndo();
  5186. TraverseWords( &PhonemeEditor::ITER_MoveSelectedWords, movetime );
  5187. PushRedo();
  5188. m_bWordsActive = true;
  5189. redraw();
  5190. Con_Printf( "Shift word %s\n", direction == -1 ? "left" : "right" );
  5191. }
  5192. void PhonemeEditor::ExtendSelectedWordEndTime( int direction )
  5193. {
  5194. if ( GetMode() != MODE_PHONEMES )
  5195. return;
  5196. CountSelected();
  5197. if ( m_nSelectedWordCount != 1 )
  5198. return;
  5199. RECT rc;
  5200. GetWorkspaceRect( rc );
  5201. // Determine start/stop positions
  5202. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  5203. float endtime = w2() / GetPixelsPerSecond() + starttime;
  5204. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  5205. float movetime = timeperpixel * (float)direction;
  5206. SetDirty( true );
  5207. PushUndo();
  5208. TraverseWords( &PhonemeEditor::ITER_ExtendSelectedWordEndTimes, movetime );
  5209. PushRedo();
  5210. m_bWordsActive = true;
  5211. redraw();
  5212. Con_Printf( "Extend word end %s\n", direction == -1 ? "left" : "right" );
  5213. }
  5214. //-----------------------------------------------------------------------------
  5215. // Purpose:
  5216. // Input : *word -
  5217. // Output : int
  5218. //-----------------------------------------------------------------------------
  5219. int PhonemeEditor::IndexOfWord( CWordTag *word )
  5220. {
  5221. for ( int i = 0 ; i < m_Tags.m_Words.Count(); i++ )
  5222. {
  5223. if ( m_Tags.m_Words[ i ] == word )
  5224. return i;
  5225. }
  5226. return -1;
  5227. }
  5228. //-----------------------------------------------------------------------------
  5229. // Purpose:
  5230. // Input : forward -
  5231. // *currentWord -
  5232. // **nextWord -
  5233. // Output : float
  5234. //-----------------------------------------------------------------------------
  5235. float PhonemeEditor::GetTimeGapToNextWord( bool forward, CWordTag *currentWord, CWordTag **ppNextWord /* = NULL */ )
  5236. {
  5237. if ( ppNextWord )
  5238. {
  5239. *ppNextWord = NULL;
  5240. }
  5241. if ( !currentWord )
  5242. return 0.0f;
  5243. int wordnum = IndexOfWord( currentWord );
  5244. if ( wordnum == -1 )
  5245. return 0.0f;
  5246. // Go in correct direction
  5247. int newwordnum = wordnum + ( forward ? 1 : -1 );
  5248. // There is no next word
  5249. if ( newwordnum >= m_Tags.m_Words.Count() )
  5250. {
  5251. return PLENTY_OF_TIME;
  5252. }
  5253. // There is no previous word
  5254. if ( newwordnum < 0 )
  5255. {
  5256. return PLENTY_OF_TIME;
  5257. }
  5258. if ( ppNextWord )
  5259. {
  5260. *ppNextWord = m_Tags.m_Words[ newwordnum ];
  5261. }
  5262. // Otherwise, figure out time gap
  5263. if ( forward )
  5264. {
  5265. float currentEnd = currentWord->m_flEndTime;
  5266. float nextStart = m_Tags.m_Words[ newwordnum ]->m_flStartTime;
  5267. return ( nextStart - currentEnd );
  5268. }
  5269. else
  5270. {
  5271. float previousEnd = m_Tags.m_Words[ newwordnum ]->m_flEndTime;
  5272. float currentStart = currentWord->m_flStartTime;
  5273. return ( currentStart - previousEnd );
  5274. }
  5275. Assert( 0 );
  5276. return 0.0f;
  5277. }
  5278. //-----------------------------------------------------------------------------
  5279. // Purpose:
  5280. // Input : forward -
  5281. // *currentPhoneme -
  5282. // **word -
  5283. // **phoneme -
  5284. // Output : float
  5285. //-----------------------------------------------------------------------------
  5286. float PhonemeEditor::GetTimeGapToNextPhoneme( bool forward, CPhonemeTag *currentPhoneme,
  5287. CWordTag **ppword /* = NULL */, CPhonemeTag **ppphoneme /* = NULL */ )
  5288. {
  5289. if ( ppword )
  5290. {
  5291. *ppword = NULL;
  5292. }
  5293. if ( ppphoneme )
  5294. {
  5295. *ppphoneme = NULL;
  5296. }
  5297. if ( !currentPhoneme )
  5298. return 0.0f;
  5299. CWordTag *word = m_Tags.GetWordForPhoneme( currentPhoneme );
  5300. if ( !word )
  5301. return 0.0f;
  5302. int wordnum = IndexOfWord( word );
  5303. Assert( wordnum != -1 );
  5304. int phonemenum = word->IndexOfPhoneme( currentPhoneme );
  5305. if ( phonemenum < 0 )
  5306. return 0.0f;
  5307. CPhonemeTag *nextPhoneme = NULL;
  5308. int nextphoneme = phonemenum + ( forward ? 1 : -1 );
  5309. // Try last phoneme of previous word
  5310. if ( nextphoneme < 0 )
  5311. {
  5312. wordnum--;
  5313. while ( wordnum >= 0 )
  5314. {
  5315. if ( ppword )
  5316. {
  5317. *ppword = m_Tags.m_Words[ wordnum ];
  5318. }
  5319. if ( m_Tags.m_Words.Count() > 0 )
  5320. {
  5321. if ( m_Tags.m_Words[ wordnum ]->m_Phonemes.Count() > 0 )
  5322. {
  5323. nextPhoneme = m_Tags.m_Words[ wordnum ]->m_Phonemes[ m_Tags.m_Words[ wordnum ]->m_Phonemes.Count() - 1 ];
  5324. break;
  5325. }
  5326. }
  5327. wordnum--;
  5328. }
  5329. }
  5330. // Try first phoneme of next word, if there is one
  5331. else if ( nextphoneme >= word->m_Phonemes.Count() )
  5332. {
  5333. wordnum++;
  5334. while ( wordnum < m_Tags.m_Words.Count() )
  5335. {
  5336. if ( ppword )
  5337. {
  5338. *ppword = m_Tags.m_Words[ wordnum ];
  5339. }
  5340. // Really it can't be zero, but check anyway
  5341. if ( m_Tags.m_Words.Count() > 0 )
  5342. {
  5343. if ( m_Tags.m_Words[ wordnum ]->m_Phonemes.Count() > 0 )
  5344. {
  5345. nextPhoneme = m_Tags.m_Words[ wordnum ]->m_Phonemes[ 0 ];
  5346. break;
  5347. }
  5348. }
  5349. wordnum++;
  5350. }
  5351. }
  5352. else
  5353. {
  5354. nextPhoneme = word->m_Phonemes[ nextphoneme ];
  5355. }
  5356. if ( !nextPhoneme )
  5357. return PLENTY_OF_TIME;
  5358. if ( ppphoneme )
  5359. {
  5360. *ppphoneme = nextPhoneme;
  5361. }
  5362. // Now compute time delta
  5363. float dt = 0.0f;
  5364. if ( forward )
  5365. {
  5366. dt = nextPhoneme->GetStartTime() - currentPhoneme->GetEndTime();
  5367. }
  5368. else
  5369. {
  5370. dt = currentPhoneme->GetStartTime() - nextPhoneme->GetEndTime();
  5371. }
  5372. return dt;
  5373. }
  5374. CPhonemeTag *PhonemeEditor::GetSelectedPhoneme( void )
  5375. {
  5376. CountSelected();
  5377. if ( m_nSelectedPhonemeCount != 1 )
  5378. return NULL;
  5379. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  5380. {
  5381. CWordTag *w = m_Tags.m_Words[ i ];
  5382. if ( !w )
  5383. continue;
  5384. for ( int j = 0; j < w->m_Phonemes.Count() ; j++ )
  5385. {
  5386. CPhonemeTag *p = w->m_Phonemes[ j ];
  5387. if ( !p || !p->m_bSelected )
  5388. continue;
  5389. return p;
  5390. }
  5391. }
  5392. return NULL;
  5393. }
  5394. CWordTag *PhonemeEditor::GetSelectedWord( void )
  5395. {
  5396. CountSelected();
  5397. if ( m_nSelectedWordCount != 1 )
  5398. return NULL;
  5399. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  5400. {
  5401. CWordTag *w = m_Tags.m_Words[ i ];
  5402. if ( !w || !w->m_bSelected )
  5403. continue;
  5404. return w;
  5405. }
  5406. return NULL;
  5407. }
  5408. void PhonemeEditor::OnMouseMove( mxEvent *event )
  5409. {
  5410. int mx = (short)event->x;
  5411. LimitDrag( mx );
  5412. event->x = (short)mx;
  5413. if ( m_nDragType != DRAGTYPE_NONE )
  5414. {
  5415. DrawFocusRect( "moving old" );
  5416. for ( int i = 0; i < m_FocusRects.Count(); i++ )
  5417. {
  5418. CFocusRect *f = &m_FocusRects[ i ];
  5419. f->m_rcFocus = f->m_rcOrig;
  5420. switch ( m_nDragType )
  5421. {
  5422. default:
  5423. {
  5424. // Only X Shifts supported
  5425. OffsetRect( &f->m_rcFocus, ( (short)event->x - m_nStartX ),
  5426. 0 );
  5427. }
  5428. break;
  5429. case DRAGTYPE_EMPHASIS_SELECT:
  5430. {
  5431. RECT rcWork;
  5432. GetWorkspaceRect( rcWork );
  5433. RECT rcEmphasis;
  5434. Emphasis_GetRect( rcWork, rcEmphasis );
  5435. RECT rcFocus;
  5436. rcFocus = f->m_rcOrig;
  5437. rcFocus.left = m_nStartX < (short)event->x ? m_nStartX : (short)event->x;
  5438. rcFocus.right = m_nStartX < (short)event->x ? (short)event->x : m_nStartX;
  5439. rcFocus.top = m_nStartY < (short)event->y ? m_nStartY : (short)event->y;
  5440. rcFocus.bottom = m_nStartY < (short)event->y ? (short)event->y : m_nStartY;
  5441. rcFocus.top = clamp( rcFocus.top, rcEmphasis.top, rcEmphasis.bottom );
  5442. rcFocus.bottom = clamp( rcFocus.bottom, rcEmphasis.top, rcEmphasis.bottom );
  5443. //OffsetRect( &rcFocus, 0, -rcEmphasis.top );
  5444. POINT offset;
  5445. offset.x = 0;
  5446. offset.y = 0;
  5447. ClientToScreen( (HWND)getHandle(), &offset );
  5448. OffsetRect( &rcFocus, offset.x, offset.y );
  5449. f->m_rcFocus = rcFocus;
  5450. }
  5451. break;
  5452. }
  5453. }
  5454. if ( m_nDragType == DRAGTYPE_EMPHASIS_MOVE )
  5455. {
  5456. redraw();
  5457. }
  5458. DrawFocusRect( "moving new" );
  5459. }
  5460. else
  5461. {
  5462. if ( m_hPrevCursor )
  5463. {
  5464. SetCursor( m_hPrevCursor );
  5465. m_hPrevCursor = NULL;
  5466. }
  5467. CountSelected();
  5468. int overhandle = IsMouseOverBoundary( event );
  5469. if ( overhandle == BOUNDARY_PHONEME && m_nSelectedPhonemeCount <= 1 )
  5470. {
  5471. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
  5472. }
  5473. else if ( overhandle == BOUNDARY_WORD && m_nSelectedWordCount <= 1 )
  5474. {
  5475. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
  5476. }
  5477. else if ( IsMouseOverSelection( (short)event->x, (short)event->y ) )
  5478. {
  5479. if ( IsMouseOverSelectionStartEdge( event ) )
  5480. {
  5481. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
  5482. }
  5483. else if ( IsMouseOverSelectionEndEdge( event ) )
  5484. {
  5485. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
  5486. }
  5487. else
  5488. {
  5489. if ( event->modifiers & mxEvent::KeyShift )
  5490. {
  5491. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
  5492. }
  5493. }
  5494. }
  5495. else
  5496. {
  5497. if ( IsMouseOverTag( (short)event->x, (short)event->y ) )
  5498. {
  5499. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
  5500. }
  5501. else
  5502. {
  5503. CPhonemeTag *pt = GetPhonemeTagUnderMouse( (short)event->x, (short)event->y );
  5504. CWordTag *wt = GetWordTagUnderMouse( (short)event->x, (short)event->y );
  5505. if ( wt || pt )
  5506. {
  5507. if ( pt )
  5508. {
  5509. // Select it
  5510. SelectExpression( pt );
  5511. }
  5512. if ( event->modifiers & mxEvent::KeyShift )
  5513. {
  5514. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
  5515. }
  5516. }
  5517. }
  5518. }
  5519. }
  5520. switch ( m_nDragType )
  5521. {
  5522. default:
  5523. break;
  5524. case DRAGTYPE_EMPHASIS_MOVE:
  5525. {
  5526. Emphasis_MouseDrag( (short)event->x, (short)event->y );
  5527. m_Tags.Resort();
  5528. }
  5529. break;
  5530. case DRAGTYPE_SCRUBBER:
  5531. {
  5532. float t = GetTimeForPixel( (short)event->x );
  5533. t += m_flScrubberTimeOffset;
  5534. ClampTimeToSelectionInterval( t );
  5535. float dt = t - m_flScrub;
  5536. SetScrubTargetTime( t );
  5537. ScrubThink( dt, true );
  5538. SetScrubTime( t );
  5539. DrawScrubHandle();
  5540. }
  5541. break;
  5542. }
  5543. m_nLastX = (short)event->x;
  5544. m_nLastY = (short)event->y;
  5545. }
  5546. //-----------------------------------------------------------------------------
  5547. // Purpose:
  5548. //-----------------------------------------------------------------------------
  5549. void PhonemeEditor::EditInsertFirstPhonemeOfWord( void )
  5550. {
  5551. if ( GetMode() != MODE_PHONEMES )
  5552. return;
  5553. CWordTag *cw = GetSelectedWord();
  5554. if ( !cw )
  5555. return;
  5556. if ( cw->m_Phonemes.Count() != 0 )
  5557. {
  5558. Con_Printf( "Can't insert first phoneme into %s, already has phonemes\n", cw->GetWord() );
  5559. return;
  5560. }
  5561. CPhonemeParams params;
  5562. memset( &params, 0, sizeof( params ) );
  5563. strcpy( params.m_szDialogTitle, "Phoneme/Viseme Properties" );
  5564. strcpy( params.m_szName, "" );
  5565. params.m_nLeft = -1;
  5566. params.m_nTop = -1;
  5567. params.m_bPositionDialog = true;
  5568. params.m_bMultiplePhoneme = true;
  5569. if ( params.m_bPositionDialog )
  5570. {
  5571. RECT rcWord;
  5572. GetWordRect( cw, rcWord );
  5573. // Convert to screen coords
  5574. POINT pt;
  5575. pt.x = rcWord.left;
  5576. pt.y = rcWord.top;
  5577. ClientToScreen( (HWND)getHandle(), &pt );
  5578. params.m_nLeft = pt.x;
  5579. params.m_nTop = pt.y;
  5580. }
  5581. int iret = PhonemeProperties( &params );
  5582. SetFocus( (HWND)getHandle() );
  5583. if ( !iret )
  5584. {
  5585. return;
  5586. }
  5587. int phonemeCount = CSentence::CountWords( params.m_szName );
  5588. if ( phonemeCount <= 0 )
  5589. {
  5590. return;
  5591. }
  5592. float wordLength = cw->m_flEndTime - cw->m_flStartTime;
  5593. float timePerPhoneme = wordLength / (float)phonemeCount;
  5594. float currentTime = cw->m_flStartTime;
  5595. SetDirty( true );
  5596. PushUndo();
  5597. unsigned char *in;
  5598. char *out;
  5599. char phonemeName[ 128 ];
  5600. in = (unsigned char *)params.m_szName;
  5601. do
  5602. {
  5603. out = phonemeName;
  5604. while ( *in > 32 )
  5605. {
  5606. *out++ = *in++;
  5607. }
  5608. *out = 0;
  5609. CPhonemeTag phoneme;
  5610. phoneme.SetPhonemeCode( TextToPhoneme( phonemeName ) );
  5611. phoneme.SetTag( phonemeName );
  5612. phoneme.SetStartTime( currentTime );
  5613. phoneme.SetEndTime( currentTime + timePerPhoneme );
  5614. phoneme.m_bSelected = false;
  5615. cw->m_Phonemes.AddToTail( new CPhonemeTag( phoneme ) );
  5616. currentTime += timePerPhoneme;
  5617. if ( !*in )
  5618. break;
  5619. // Skip whitespace
  5620. in++;
  5621. } while ( 1 );
  5622. cw->m_Phonemes[ 0 ]->m_bSelected = true;
  5623. PushRedo();
  5624. // Add it
  5625. redraw();
  5626. }
  5627. void PhonemeEditor::SelectPhonemes( bool forward )
  5628. {
  5629. if ( GetMode() != MODE_PHONEMES )
  5630. return;
  5631. CountSelected();
  5632. if ( m_nSelectedPhonemeCount != 1 )
  5633. return;
  5634. CPhonemeTag *phoneme = GetSelectedPhoneme();
  5635. if ( !phoneme )
  5636. return;
  5637. // Figure out it's word and index
  5638. CWordTag *word = m_Tags.GetWordForPhoneme( phoneme );
  5639. if ( !word )
  5640. return;
  5641. int wordNum = IndexOfWord( word );
  5642. if ( wordNum == -1 )
  5643. return;
  5644. // Select remaining phonemes in current word
  5645. int i;
  5646. i = word->IndexOfPhoneme( phoneme );
  5647. if ( i == -1 )
  5648. return;
  5649. if ( forward )
  5650. {
  5651. // Start at next one
  5652. i++;
  5653. for ( ; i < word->m_Phonemes.Count(); i++ )
  5654. {
  5655. phoneme = word->m_Phonemes[ i ];
  5656. phoneme->m_bSelected = true;
  5657. }
  5658. // Now start at next word
  5659. wordNum++;
  5660. for ( ; wordNum < m_Tags.m_Words.Count(); wordNum++ )
  5661. {
  5662. word = m_Tags.m_Words[ wordNum ];
  5663. for ( int j = 0; j < word->m_Phonemes.Count(); j++ )
  5664. {
  5665. phoneme = word->m_Phonemes[ j ];
  5666. phoneme->m_bSelected = true;
  5667. }
  5668. }
  5669. }
  5670. else
  5671. {
  5672. // Start at previous
  5673. i--;
  5674. for ( ; i >= 0; i-- )
  5675. {
  5676. phoneme = word->m_Phonemes[ i ];
  5677. phoneme->m_bSelected = true;
  5678. }
  5679. // Now start at previous word
  5680. wordNum--;
  5681. for ( ; wordNum >= 0 ; wordNum-- )
  5682. {
  5683. word = m_Tags.m_Words[ wordNum ];
  5684. for ( int j = 0; j < word->m_Phonemes.Count(); j++ )
  5685. {
  5686. phoneme = word->m_Phonemes[ j ];
  5687. phoneme->m_bSelected = true;
  5688. }
  5689. }
  5690. }
  5691. redraw();
  5692. }
  5693. void PhonemeEditor::SelectWords( bool forward )
  5694. {
  5695. if ( GetMode() != MODE_PHONEMES )
  5696. return;
  5697. CountSelected();
  5698. if ( m_nSelectedWordCount != 1 )
  5699. return;
  5700. // Figure out it's word and index
  5701. CWordTag *word = GetSelectedWord();
  5702. if ( !word )
  5703. return;
  5704. int wordNum = IndexOfWord( word );
  5705. if ( wordNum == -1 )
  5706. return;
  5707. if ( forward )
  5708. {
  5709. wordNum++;
  5710. for ( ; wordNum < m_Tags.m_Words.Count(); wordNum++ )
  5711. {
  5712. word = m_Tags.m_Words[ wordNum ];
  5713. word->m_bSelected = true;
  5714. }
  5715. }
  5716. else
  5717. {
  5718. wordNum--;
  5719. for ( ; wordNum >= 0; wordNum-- )
  5720. {
  5721. word = m_Tags.m_Words[ wordNum ];
  5722. word->m_bSelected = true;
  5723. }
  5724. }
  5725. redraw();
  5726. }
  5727. bool PhonemeEditor::AreSelectedWordsContiguous( void )
  5728. {
  5729. CountSelected();
  5730. if ( m_nSelectedWordCount < 1 )
  5731. return false;
  5732. if ( m_nSelectedWordCount == 1 )
  5733. return true;
  5734. // Find first selected word
  5735. int runcount = 0;
  5736. bool parity = false;
  5737. for ( int i = 0 ; i < m_Tags.m_Words.Count() ; i++ )
  5738. {
  5739. CWordTag *word = m_Tags.m_Words[ i ];
  5740. if ( !word )
  5741. continue;
  5742. if ( word->m_bSelected )
  5743. {
  5744. if ( !parity )
  5745. {
  5746. parity = true;
  5747. runcount++;
  5748. }
  5749. }
  5750. else
  5751. {
  5752. if ( parity )
  5753. {
  5754. parity = false;
  5755. }
  5756. }
  5757. }
  5758. if ( runcount == 1 )
  5759. return true;
  5760. return false;
  5761. }
  5762. bool PhonemeEditor::AreSelectedPhonemesContiguous( void )
  5763. {
  5764. CountSelected();
  5765. if ( m_nSelectedPhonemeCount < 1 )
  5766. return false;
  5767. if ( m_nSelectedPhonemeCount == 1 )
  5768. return true;
  5769. // Find first selected word
  5770. int runcount = 0;
  5771. bool parity = false;
  5772. for ( int i = 0 ; i < m_Tags.m_Words.Count() ; i++ )
  5773. {
  5774. CWordTag *word = m_Tags.m_Words[ i ];
  5775. if ( !word )
  5776. continue;
  5777. for ( int j = 0 ; j < word->m_Phonemes.Count(); j++ )
  5778. {
  5779. CPhonemeTag *phoneme = word->m_Phonemes[ j ];
  5780. if ( !phoneme )
  5781. continue;
  5782. if ( phoneme->m_bSelected )
  5783. {
  5784. if ( !parity )
  5785. {
  5786. parity = true;
  5787. runcount++;
  5788. }
  5789. }
  5790. else
  5791. {
  5792. if ( parity )
  5793. {
  5794. parity = false;
  5795. }
  5796. }
  5797. }
  5798. }
  5799. if ( runcount == 1 )
  5800. return true;
  5801. return false;
  5802. }
  5803. void PhonemeEditor::SortWords( bool prepareundo )
  5804. {
  5805. if ( prepareundo )
  5806. {
  5807. SetDirty( true );
  5808. PushUndo();
  5809. }
  5810. // Just bubble sort by start time
  5811. int c = m_Tags.m_Words.Count();
  5812. int i;
  5813. // check for start > end
  5814. for ( i = 0; i < c; i++ )
  5815. {
  5816. CWordTag *p1 = m_Tags.m_Words[ i ];
  5817. if (p1->m_flStartTime > p1->m_flEndTime )
  5818. {
  5819. float swap = p1->m_flStartTime;
  5820. p1->m_flStartTime = p1->m_flEndTime;
  5821. p1->m_flEndTime = swap;
  5822. }
  5823. }
  5824. for ( i = 0; i < c; i++ )
  5825. {
  5826. for ( int j = i + 1; j < c; j++ )
  5827. {
  5828. CWordTag *p1 = m_Tags.m_Words[ i ];
  5829. CWordTag *p2 = m_Tags.m_Words[ j ];
  5830. if ( p1->m_flStartTime < p2->m_flStartTime )
  5831. continue;
  5832. // Swap them
  5833. m_Tags.m_Words[ i ] = p2;
  5834. m_Tags.m_Words[ j ] = p1;
  5835. }
  5836. }
  5837. if ( prepareundo )
  5838. {
  5839. PushRedo();
  5840. }
  5841. }
  5842. void PhonemeEditor::SortPhonemes( bool prepareundo )
  5843. {
  5844. if ( prepareundo )
  5845. {
  5846. SetDirty( true );
  5847. PushUndo();
  5848. }
  5849. // Just bubble sort by start time
  5850. int wc = m_Tags.m_Words.Count();
  5851. for ( int w = 0; w < wc; w++ )
  5852. {
  5853. CWordTag *word = m_Tags.m_Words[ w ];
  5854. Assert( word );
  5855. int c = word->m_Phonemes.Count();
  5856. int i;
  5857. // check for start > end
  5858. for ( i = 0; i < c; i++ )
  5859. {
  5860. CPhonemeTag *p1 = word->m_Phonemes[ i ];
  5861. if (p1->GetStartTime() > p1->GetEndTime() )
  5862. {
  5863. float swap = p1->GetStartTime();
  5864. p1->SetStartTime( p1->GetEndTime() );
  5865. p1->SetEndTime( swap );
  5866. }
  5867. }
  5868. for ( i = 0; i < c; i++ )
  5869. {
  5870. for ( int j = i + 1; j < c; j++ )
  5871. {
  5872. CPhonemeTag *p1 = word->m_Phonemes[ i ];
  5873. CPhonemeTag *p2 = word->m_Phonemes[ j ];
  5874. if ( p1->GetStartTime() < p2->GetStartTime() )
  5875. continue;
  5876. // Swap them
  5877. word->m_Phonemes[ i ] = p2;
  5878. word->m_Phonemes[ j ] = p1;
  5879. }
  5880. }
  5881. }
  5882. if ( prepareundo )
  5883. {
  5884. PushRedo();
  5885. }
  5886. }
  5887. void PhonemeEditor::CleanupWordsAndPhonemes( bool prepareundo )
  5888. {
  5889. if ( GetMode() != MODE_PHONEMES )
  5890. return;
  5891. // 2 pixel gap
  5892. float snap_epsilon = 2.49f / GetPixelsPerSecond();
  5893. if ( prepareundo )
  5894. {
  5895. SetDirty( true );
  5896. PushUndo();
  5897. }
  5898. SortWords( false );
  5899. SortPhonemes( false );
  5900. for ( int i = 0 ; i < m_Tags.m_Words.Count() ; i++ )
  5901. {
  5902. CWordTag *word = m_Tags.m_Words[ i ];
  5903. if ( !word )
  5904. continue;
  5905. CWordTag *next = NULL;
  5906. if ( i < m_Tags.m_Words.Count() - 1 )
  5907. {
  5908. next = m_Tags.m_Words[ i + 1 ];
  5909. }
  5910. if ( word && next )
  5911. {
  5912. // Check for words close enough
  5913. float eps = next->m_flStartTime - word->m_flEndTime;
  5914. if ( eps && eps <= snap_epsilon )
  5915. {
  5916. float t = (word->m_flEndTime + next->m_flStartTime) * 0.5;
  5917. word->m_flEndTime = t;
  5918. next->m_flStartTime = t;
  5919. }
  5920. }
  5921. for ( int j = 0 ; j < word->m_Phonemes.Count(); j++ )
  5922. {
  5923. CPhonemeTag *phoneme = word->m_Phonemes[ j ];
  5924. if ( !phoneme )
  5925. continue;
  5926. CPhonemeTag *next = NULL;
  5927. if ( j < word->m_Phonemes.Count() - 1 )
  5928. {
  5929. next = word->m_Phonemes[ j + 1 ];
  5930. }
  5931. if ( phoneme && next )
  5932. {
  5933. float eps = next->GetStartTime() - phoneme->GetEndTime();
  5934. if ( eps && eps <= snap_epsilon )
  5935. {
  5936. float t = (phoneme->GetEndTime() + next->GetStartTime() ) * 0.5;
  5937. phoneme->SetEndTime( t );
  5938. next->SetStartTime( t );
  5939. }
  5940. }
  5941. }
  5942. }
  5943. if ( prepareundo )
  5944. {
  5945. PushRedo();
  5946. }
  5947. // NOTE: Caller must call "redraw()" to get screen to update
  5948. }
  5949. void PhonemeEditor::RealignPhonemesToWords( bool prepareundo )
  5950. {
  5951. if ( GetMode() != MODE_PHONEMES )
  5952. return;
  5953. if ( prepareundo )
  5954. {
  5955. SetDirty( true );
  5956. PushUndo();
  5957. }
  5958. SortWords( false );
  5959. SortPhonemes( false );
  5960. for ( int i = 0 ; i < m_Tags.m_Words.Count() ; i++ )
  5961. {
  5962. CWordTag *word = m_Tags.m_Words[ i ];
  5963. if ( !word )
  5964. continue;
  5965. CWordTag *next = NULL;
  5966. if ( i < m_Tags.m_Words.Count() - 1 )
  5967. {
  5968. next = m_Tags.m_Words[ i + 1 ];
  5969. }
  5970. float word_dt = word->m_flEndTime - word->m_flStartTime;
  5971. CPhonemeTag *FirstPhoneme = word->m_Phonemes[ 0 ];
  5972. if ( !FirstPhoneme )
  5973. continue;
  5974. CPhonemeTag *LastPhoneme = word->m_Phonemes[ word->m_Phonemes.Count() - 1 ];
  5975. if ( !LastPhoneme )
  5976. continue;
  5977. float phoneme_dt = LastPhoneme->GetEndTime() - FirstPhoneme->GetStartTime();
  5978. float phoneme_shift = FirstPhoneme->GetStartTime();
  5979. for ( int j = 0 ; j < word->m_Phonemes.Count(); j++ )
  5980. {
  5981. CPhonemeTag *phoneme = word->m_Phonemes[ j ];
  5982. if ( !phoneme )
  5983. continue;
  5984. CPhonemeTag *next = NULL;
  5985. if ( j < word->m_Phonemes.Count() - 1 )
  5986. {
  5987. next = word->m_Phonemes[ j + 1 ];
  5988. }
  5989. if (j == 0)
  5990. {
  5991. float t = (phoneme->GetStartTime() - phoneme_shift ) * (word_dt / phoneme_dt) + word->m_flStartTime;
  5992. phoneme->SetStartTime( t );
  5993. }
  5994. float t = (phoneme->GetEndTime() - phoneme_shift ) * (word_dt / phoneme_dt) + word->m_flStartTime;
  5995. phoneme->SetEndTime( t );
  5996. if (next)
  5997. {
  5998. next->SetStartTime( t );
  5999. }
  6000. }
  6001. }
  6002. if ( prepareundo )
  6003. {
  6004. PushRedo();
  6005. }
  6006. // NOTE: Caller must call "redraw()" to get screen to update
  6007. }
  6008. void PhonemeEditor::RealignWordsToPhonemes( bool prepareundo )
  6009. {
  6010. if ( GetMode() != MODE_PHONEMES )
  6011. return;
  6012. if ( prepareundo )
  6013. {
  6014. SetDirty( true );
  6015. PushUndo();
  6016. }
  6017. SortWords( false );
  6018. SortPhonemes( false );
  6019. for ( int i = 0 ; i < m_Tags.m_Words.Count() ; i++ )
  6020. {
  6021. CWordTag *word = m_Tags.m_Words[ i ];
  6022. if ( !word )
  6023. continue;
  6024. CPhonemeTag *FirstPhoneme = word->m_Phonemes[ 0 ];
  6025. if ( !FirstPhoneme )
  6026. continue;
  6027. CPhonemeTag *LastPhoneme = word->m_Phonemes[ word->m_Phonemes.Count() - 1 ];
  6028. if ( !LastPhoneme )
  6029. continue;
  6030. word->m_flStartTime = FirstPhoneme->GetStartTime();
  6031. word->m_flEndTime = LastPhoneme->GetEndTime();
  6032. }
  6033. if ( prepareundo )
  6034. {
  6035. PushRedo();
  6036. }
  6037. // NOTE: Caller must call "redraw()" to get screen to update
  6038. }
  6039. float PhonemeEditor::ComputeMaxWordShift( bool forward, bool allowcrop )
  6040. {
  6041. // skipping selected words, figure out max time shift of words before they selection touches any
  6042. // unselected words
  6043. // if allowcrop is true, then the maximum extends up to end of next word
  6044. float maxshift = PLENTY_OF_TIME;
  6045. if ( forward )
  6046. {
  6047. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  6048. {
  6049. CWordTag *w1 = m_Tags.m_Words[ i ];
  6050. if ( !w1 || !w1->m_bSelected )
  6051. continue;
  6052. CWordTag *w2 = NULL;
  6053. for ( int search = i + 1; search < m_Tags.m_Words.Count() ; search++ )
  6054. {
  6055. CWordTag *check = m_Tags.m_Words[ search ];
  6056. if ( !check || check->m_bSelected )
  6057. continue;
  6058. w2 = check;
  6059. break;
  6060. }
  6061. if ( w2 )
  6062. {
  6063. float shift;
  6064. if ( allowcrop )
  6065. {
  6066. shift = w2->m_flEndTime - w1->m_flEndTime;
  6067. }
  6068. else
  6069. {
  6070. shift = w2->m_flStartTime - w1->m_flEndTime;
  6071. }
  6072. if ( shift < maxshift )
  6073. {
  6074. maxshift = shift;
  6075. }
  6076. }
  6077. }
  6078. }
  6079. else
  6080. {
  6081. for ( int i = m_Tags.m_Words.Count() -1; i >= 0; i-- )
  6082. {
  6083. CWordTag *w1 = m_Tags.m_Words[ i ];
  6084. if ( !w1 || !w1->m_bSelected )
  6085. continue;
  6086. CWordTag *w2 = NULL;
  6087. for ( int search = i - 1; search >= 0 ; search-- )
  6088. {
  6089. CWordTag *check = m_Tags.m_Words[ search ];
  6090. if ( !check || check->m_bSelected )
  6091. continue;
  6092. w2 = check;
  6093. break;
  6094. }
  6095. if ( w2 )
  6096. {
  6097. float shift;
  6098. if ( allowcrop )
  6099. {
  6100. shift = w1->m_flStartTime - w2->m_flStartTime;
  6101. }
  6102. else
  6103. {
  6104. shift = w1->m_flStartTime - w2->m_flEndTime;
  6105. }
  6106. if ( shift < maxshift )
  6107. {
  6108. maxshift = shift;
  6109. }
  6110. }
  6111. }
  6112. }
  6113. return maxshift;
  6114. }
  6115. float PhonemeEditor::ComputeMaxPhonemeShift( bool forward, bool allowcrop )
  6116. {
  6117. // skipping selected phonemes, figure out max time shift of phonemes before they selection touches any
  6118. // unselected words
  6119. // if allowcrop is true, then the maximum extends up to end of next word
  6120. float maxshift = PLENTY_OF_TIME;
  6121. if ( forward )
  6122. {
  6123. for ( int i = 0; i < m_Tags.m_Words.Count(); i++ )
  6124. {
  6125. CWordTag *word = m_Tags.m_Words[ i ];
  6126. if ( !word )
  6127. continue;
  6128. for ( int j = 0; j < word->m_Phonemes.Count(); j++ )
  6129. {
  6130. CPhonemeTag *p1 = word->m_Phonemes[ j ];
  6131. if ( !p1 || !p1->m_bSelected )
  6132. continue;
  6133. // Find next unselected phoneme
  6134. CPhonemeTag *p2 = NULL;
  6135. CPhonemeTag *start = p1;
  6136. do
  6137. {
  6138. CPhonemeTag *test = NULL;
  6139. GetTimeGapToNextPhoneme( forward, start, NULL, &test );
  6140. if ( !test )
  6141. break;
  6142. if ( test->m_bSelected )
  6143. {
  6144. start = test;
  6145. continue;
  6146. }
  6147. p2 = test;
  6148. break;
  6149. } while ( 1 );
  6150. if ( p2 )
  6151. {
  6152. float shift;
  6153. if ( allowcrop )
  6154. {
  6155. shift = p2->GetEndTime() - p1->GetEndTime();
  6156. }
  6157. else
  6158. {
  6159. shift = p2->GetStartTime() - p1->GetEndTime();
  6160. }
  6161. if ( shift < maxshift )
  6162. {
  6163. maxshift = shift;
  6164. }
  6165. }
  6166. }
  6167. }
  6168. }
  6169. else
  6170. {
  6171. for ( int i = m_Tags.m_Words.Count() -1; i >= 0; i-- )
  6172. {
  6173. CWordTag *word = m_Tags.m_Words[ i ];
  6174. if ( !word )
  6175. continue;
  6176. for ( int j = word->m_Phonemes.Count() - 1; j >= 0; j-- )
  6177. {
  6178. CPhonemeTag *p1 = word->m_Phonemes[ j ];
  6179. if ( !p1 || !p1->m_bSelected )
  6180. continue;
  6181. // Find previous unselected phoneme
  6182. CPhonemeTag *p2 = NULL;
  6183. CPhonemeTag *start = p1;
  6184. do
  6185. {
  6186. CPhonemeTag *test = NULL;
  6187. GetTimeGapToNextPhoneme( forward, start, NULL, &test );
  6188. if ( !test )
  6189. break;
  6190. if ( test->m_bSelected )
  6191. {
  6192. start = test;
  6193. continue;
  6194. }
  6195. p2 = test;
  6196. break;
  6197. } while ( 1 );
  6198. if ( p2 )
  6199. {
  6200. float shift;
  6201. if ( allowcrop )
  6202. {
  6203. shift = p1->GetStartTime() - p2->GetStartTime();
  6204. }
  6205. else
  6206. {
  6207. shift = p1->GetStartTime() - p2->GetEndTime();
  6208. }
  6209. if ( shift < maxshift )
  6210. {
  6211. maxshift = shift;
  6212. }
  6213. }
  6214. }
  6215. }
  6216. }
  6217. return maxshift;
  6218. }
  6219. int PhonemeEditor::PixelsForDeltaTime( float dt )
  6220. {
  6221. if ( !dt )
  6222. return 0;
  6223. RECT rc;
  6224. GetWorkspaceRect( rc );
  6225. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  6226. float endtime = w2() / GetPixelsPerSecond() + starttime;
  6227. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  6228. float pixels = dt / timeperpixel;
  6229. return abs( (int)pixels );
  6230. }
  6231. void PhonemeEditor::ClearDragLimit( void )
  6232. {
  6233. m_bLimitDrag = false;
  6234. m_nLeftLimit = -1;
  6235. m_nRightLimit = -1;
  6236. }
  6237. void PhonemeEditor::SetDragLimit( int dragtype )
  6238. {
  6239. ClearDragLimit();
  6240. float nextW, nextP;
  6241. float prevW, prevP;
  6242. nextW = ComputeMaxWordShift( true, false );
  6243. prevW = ComputeMaxWordShift( false, false );
  6244. nextP = ComputeMaxPhonemeShift( true, false );
  6245. prevP = ComputeMaxPhonemeShift( false, false );
  6246. /*
  6247. Con_Printf( "+w %f -w %f +p %f -p %f\n",
  6248. 1000.0f * nextW,
  6249. 1000.0f * prevW,
  6250. 1000.0f * nextP,
  6251. 1000.0f * prevP );
  6252. */
  6253. switch ( dragtype )
  6254. {
  6255. case DRAGTYPE_MOVEWORD:
  6256. m_bLimitDrag = true;
  6257. m_nLeftLimit = PixelsForDeltaTime( prevW );
  6258. m_nRightLimit = PixelsForDeltaTime( nextW );
  6259. break;
  6260. case DRAGTYPE_MOVEPHONEME:
  6261. m_bLimitDrag = true;
  6262. m_nLeftLimit = PixelsForDeltaTime( prevP );
  6263. m_nRightLimit = PixelsForDeltaTime( nextP );
  6264. break;
  6265. default:
  6266. ClearDragLimit();
  6267. break;
  6268. }
  6269. }
  6270. void PhonemeEditor::LimitDrag( int& mousex )
  6271. {
  6272. if ( m_nDragType == DRAGTYPE_NONE )
  6273. return;
  6274. if ( !m_bLimitDrag )
  6275. return;
  6276. int delta = mousex - m_nStartX;
  6277. if ( delta > 0 )
  6278. {
  6279. if ( m_nRightLimit >= 0 )
  6280. {
  6281. if ( delta > m_nRightLimit )
  6282. {
  6283. mousex = m_nStartX + m_nRightLimit;
  6284. }
  6285. }
  6286. }
  6287. else if ( delta < 0 )
  6288. {
  6289. if ( m_nLeftLimit >= 0 )
  6290. {
  6291. if ( abs( delta ) > abs( m_nLeftLimit ) )
  6292. {
  6293. mousex = m_nStartX - m_nLeftLimit;
  6294. }
  6295. }
  6296. }
  6297. }
  6298. //-----------------------------------------------------------------------------
  6299. // Purpose: Wipe undo/redo data
  6300. //-----------------------------------------------------------------------------
  6301. void PhonemeEditor::ClearUndo( void )
  6302. {
  6303. WipeUndo();
  6304. WipeRedo();
  6305. SetDirty( false );
  6306. }
  6307. //-----------------------------------------------------------------------------
  6308. // Purpose:
  6309. // Input : *tag -
  6310. //-----------------------------------------------------------------------------
  6311. void PhonemeEditor::SelectExpression( CPhonemeTag *tag )
  6312. {
  6313. if ( !models->GetActiveStudioModel() )
  6314. return;
  6315. CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
  6316. if ( !hdr )
  6317. return;
  6318. // Make sure phonemes are loaded
  6319. FacePoser_EnsurePhonemesLoaded();
  6320. CExpClass *cl = expressions->FindClass( "phonemes", true );
  6321. if ( !cl )
  6322. {
  6323. Con_Printf( "Couldn't load expressions/phonemes.txt!\n" );
  6324. return;
  6325. }
  6326. if ( expressions->GetActiveClass() != cl )
  6327. {
  6328. expressions->ActivateExpressionClass( cl );
  6329. }
  6330. CExpression *exp = cl->FindExpression( ConvertPhoneme( tag->GetPhonemeCode() ) );
  6331. if ( !exp )
  6332. {
  6333. Con_Printf( "Couldn't find phoneme '%s'\n", ConvertPhoneme( tag->GetPhonemeCode() ) );
  6334. return;
  6335. }
  6336. float *settings = exp->GetSettings();
  6337. for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
  6338. {
  6339. int j = hdr->pFlexcontroller( i )->localToGlobal;
  6340. models->GetActiveStudioModel()->SetFlexController( i, settings[j] );
  6341. }
  6342. }
  6343. void PhonemeEditor::OnSAPI( void )
  6344. {
  6345. if ( GetMode() != MODE_PHONEMES )
  6346. return;
  6347. g_viewerSettings.speechapiindex = SPEECH_API_SAPI;
  6348. m_pPhonemeExtractor = NULL;
  6349. CheckSpeechAPI();
  6350. redraw();
  6351. }
  6352. void PhonemeEditor::OnLipSinc( void )
  6353. {
  6354. if ( GetMode() != MODE_PHONEMES )
  6355. return;
  6356. g_viewerSettings.speechapiindex = SPEECH_API_LIPSINC;
  6357. m_pPhonemeExtractor = NULL;
  6358. CheckSpeechAPI();
  6359. redraw();
  6360. }
  6361. void PhonemeEditor::LoadPhonemeConverters()
  6362. {
  6363. m_pPhonemeExtractor = NULL;
  6364. // Enumerate modules under bin folder of exe
  6365. FileFindHandle_t findHandle;
  6366. const char *pFilename = filesystem->FindFirstEx( "phonemeextractors/*.dll", "EXECUTABLE_PATH", &findHandle );
  6367. while( pFilename )
  6368. {
  6369. char fullpath[ 512 ];
  6370. Q_snprintf( fullpath, sizeof( fullpath ), "phonemeextractors/%s", pFilename );
  6371. Con_Printf( "Loading extractor from %s\n", fullpath );
  6372. Extractor e;
  6373. e.module = Sys_LoadModule( fullpath );
  6374. if ( !e.module )
  6375. {
  6376. pFilename = filesystem->FindNext( findHandle );
  6377. continue;
  6378. }
  6379. CreateInterfaceFn factory = Sys_GetFactory( e.module );
  6380. if ( !factory )
  6381. {
  6382. pFilename = filesystem->FindNext( findHandle );
  6383. continue;
  6384. }
  6385. e.extractor = ( IPhonemeExtractor * )factory( VPHONEME_EXTRACTOR_INTERFACE, NULL );
  6386. if ( !e.extractor )
  6387. {
  6388. Warning( "Unable to get IPhonemeExtractor interface version %s from %s\n", VPHONEME_EXTRACTOR_INTERFACE, fullpath );
  6389. pFilename = filesystem->FindNext( findHandle );
  6390. continue;
  6391. }
  6392. e.apitype = e.extractor->GetAPIType();
  6393. g_Extractors.AddToTail( e );
  6394. pFilename = filesystem->FindNext( findHandle );
  6395. }
  6396. filesystem->FindClose( findHandle );
  6397. }
  6398. void PhonemeEditor::ValidateSpeechAPIIndex()
  6399. {
  6400. if ( !DoesExtractorExistFor( (PE_APITYPE)g_viewerSettings.speechapiindex ) )
  6401. {
  6402. if ( g_Extractors.Count() > 0 )
  6403. g_viewerSettings.speechapiindex = g_Extractors[0].apitype;
  6404. }
  6405. }
  6406. void PhonemeEditor::UnloadPhonemeConverters()
  6407. {
  6408. int c = g_Extractors.Count();
  6409. for ( int i = c - 1; i >= 0; i-- )
  6410. {
  6411. Extractor *e = &g_Extractors[ i ];
  6412. Sys_UnloadModule( e->module );
  6413. }
  6414. g_Extractors.RemoveAll();
  6415. m_pPhonemeExtractor = NULL;
  6416. }
  6417. bool PhonemeEditor::CheckSpeechAPI( void )
  6418. {
  6419. if ( GetMode() != MODE_PHONEMES )
  6420. {
  6421. return false;
  6422. }
  6423. if ( !m_pPhonemeExtractor )
  6424. {
  6425. int c = g_Extractors.Count();
  6426. for ( int i = 0; i < c; i++ )
  6427. {
  6428. Extractor *e = &g_Extractors[ i ];
  6429. if ( e->apitype == (PE_APITYPE)g_viewerSettings.speechapiindex )
  6430. {
  6431. m_pPhonemeExtractor = e->extractor;
  6432. break;
  6433. }
  6434. }
  6435. if ( !m_pPhonemeExtractor )
  6436. {
  6437. Con_ErrorPrintf( "Couldn't find phoneme extractor %i\n",
  6438. g_viewerSettings.speechapiindex );
  6439. }
  6440. }
  6441. return m_pPhonemeExtractor != NULL;
  6442. }
  6443. //-----------------------------------------------------------------------------
  6444. // Purpose:
  6445. // Output : char const
  6446. //-----------------------------------------------------------------------------
  6447. char const *PhonemeEditor::GetSpeechAPIName( void )
  6448. {
  6449. CheckSpeechAPI();
  6450. if ( m_pPhonemeExtractor )
  6451. {
  6452. return m_pPhonemeExtractor->GetName();
  6453. }
  6454. return "Unknown Speech API";
  6455. }
  6456. //-----------------------------------------------------------------------------
  6457. // Purpose:
  6458. // Output : Returns true on success, false on failure.
  6459. //-----------------------------------------------------------------------------
  6460. bool PhonemeEditor::PaintBackground( void )
  6461. {
  6462. redraw();
  6463. return false;
  6464. }
  6465. //-----------------------------------------------------------------------------
  6466. // Purpose:
  6467. // Output : PhonemeEditor::EditorMode
  6468. //-----------------------------------------------------------------------------
  6469. PhonemeEditor::EditorMode PhonemeEditor::GetMode( void ) const
  6470. {
  6471. return m_CurrentMode;
  6472. }
  6473. //-----------------------------------------------------------------------------
  6474. // Purpose:
  6475. // Input : rcWorkSpace -
  6476. // rcEmphasis -
  6477. //-----------------------------------------------------------------------------
  6478. void PhonemeEditor::Emphasis_GetRect( RECT const & rcWorkSpace, RECT& rcEmphasis )
  6479. {
  6480. rcEmphasis = rcWorkSpace;
  6481. int ybottom = rcWorkSpace.bottom - 2 * m_nTickHeight - 2;
  6482. int workspaceheight = rcWorkSpace.bottom - rcWorkSpace.top;
  6483. // Just past midpoint
  6484. rcEmphasis.top = rcWorkSpace.top + workspaceheight / 2 + 2;
  6485. // 60 units or
  6486. rcEmphasis.bottom = clamp( rcEmphasis.top + 60, rcEmphasis.top + 20, ybottom );
  6487. }
  6488. //-----------------------------------------------------------------------------
  6489. // Purpose:
  6490. //-----------------------------------------------------------------------------
  6491. void PhonemeEditor::OnModeChanged( void )
  6492. {
  6493. // Show/hide controls as necessary
  6494. }
  6495. //-----------------------------------------------------------------------------
  6496. // Purpose:
  6497. // Input : *parent -
  6498. //-----------------------------------------------------------------------------
  6499. void PhonemeEditor::Emphasis_Init( void )
  6500. {
  6501. m_nNumSelected = 0;
  6502. }
  6503. CEmphasisSample *PhonemeEditor::Emphasis_GetSampleUnderMouse( mxEvent *event )
  6504. {
  6505. if ( GetMode() != MODE_EMPHASIS )
  6506. return NULL;
  6507. if ( !m_pWaveFile )
  6508. return NULL;
  6509. if ( w2() <= 0 )
  6510. return NULL;
  6511. if ( GetPixelsPerSecond() <= 0 )
  6512. return NULL;
  6513. float timeperpixel = 1.0f / GetPixelsPerSecond();
  6514. float closest_dist = 999999.0f;
  6515. CEmphasisSample *bestsample = NULL;
  6516. int samples = m_Tags.GetNumSamples();
  6517. float clickTime = GetTimeForPixel( (short)event->x );
  6518. for ( int i = 0; i < samples; i++ )
  6519. {
  6520. CEmphasisSample *sample = m_Tags.GetSample( i );
  6521. float dist = fabs( sample->time - clickTime );
  6522. if ( dist < closest_dist )
  6523. {
  6524. bestsample = sample;
  6525. closest_dist = dist;
  6526. }
  6527. }
  6528. // Not close to any of them!!!
  6529. if ( closest_dist > ( 5.0f * timeperpixel ) )
  6530. {
  6531. return NULL;
  6532. }
  6533. return bestsample;
  6534. }
  6535. //-----------------------------------------------------------------------------
  6536. // Purpose:
  6537. //-----------------------------------------------------------------------------
  6538. void PhonemeEditor::Emphasis_DeselectAll( void )
  6539. {
  6540. if ( GetMode() != MODE_EMPHASIS )
  6541. return;
  6542. for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
  6543. {
  6544. CEmphasisSample *sample = m_Tags.GetSample( i );
  6545. sample->selected = false;
  6546. }
  6547. redraw();
  6548. }
  6549. //-----------------------------------------------------------------------------
  6550. // Purpose:
  6551. //-----------------------------------------------------------------------------
  6552. void PhonemeEditor::Emphasis_SelectAll( void )
  6553. {
  6554. if ( GetMode() != MODE_EMPHASIS )
  6555. return;
  6556. for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
  6557. {
  6558. CEmphasisSample *sample = m_Tags.GetSample( i );
  6559. sample->selected = true;
  6560. }
  6561. redraw();
  6562. }
  6563. //-----------------------------------------------------------------------------
  6564. // Purpose:
  6565. //-----------------------------------------------------------------------------
  6566. void PhonemeEditor::Emphasis_Delete( void )
  6567. {
  6568. if ( GetMode() != MODE_EMPHASIS )
  6569. return;
  6570. SetDirty( true );
  6571. PushUndo();
  6572. for ( int i = m_Tags.GetNumSamples() - 1; i >= 0 ; i-- )
  6573. {
  6574. CEmphasisSample *sample = m_Tags.GetSample( i );
  6575. if ( !sample->selected )
  6576. continue;
  6577. m_Tags.m_EmphasisSamples.Remove( i );
  6578. SetDirty( true );
  6579. }
  6580. PushRedo();
  6581. redraw();
  6582. }
  6583. //-----------------------------------------------------------------------------
  6584. // Purpose:
  6585. // Input : sample -
  6586. //-----------------------------------------------------------------------------
  6587. void PhonemeEditor::Emphasis_AddSample( CEmphasisSample const& sample )
  6588. {
  6589. if ( GetMode() != MODE_EMPHASIS )
  6590. return;
  6591. SetDirty( true );
  6592. PushUndo();
  6593. m_Tags.m_EmphasisSamples.AddToTail( sample );
  6594. m_Tags.Resort();
  6595. PushRedo();
  6596. redraw();
  6597. }
  6598. //-----------------------------------------------------------------------------
  6599. // Purpose:
  6600. //-----------------------------------------------------------------------------
  6601. void PhonemeEditor::Emphasis_CountSelected( void )
  6602. {
  6603. m_nNumSelected = 0;
  6604. for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
  6605. {
  6606. CEmphasisSample *sample = m_Tags.GetSample( i );
  6607. if ( !sample || !sample->selected )
  6608. continue;
  6609. m_nNumSelected++;
  6610. }
  6611. }
  6612. void PhonemeEditor::Emphasis_ShowContextMenu( mxEvent *event )
  6613. {
  6614. if ( GetMode() != MODE_EMPHASIS )
  6615. return;
  6616. CountSelected();
  6617. // Construct main menu
  6618. mxPopupMenu *pop = new mxPopupMenu();
  6619. if ( m_nNumSelected > 0 )
  6620. {
  6621. pop->add( va( "Delete" ), IDC_EMPHASIS_DELETE );
  6622. pop->add( "Deselect all", IDC_EMPHASIS_DESELECT );
  6623. }
  6624. pop->add( "Select all", IDC_EMPHASIS_SELECTALL );
  6625. pop->popup( this, (short)event->x, (short)event->y );
  6626. }
  6627. void PhonemeEditor::Emphasis_MouseDrag( int x, int y )
  6628. {
  6629. if ( m_nDragType != DRAGTYPE_EMPHASIS_MOVE )
  6630. return;
  6631. RECT rcWork;
  6632. GetWorkspaceRect( rcWork );
  6633. RECT rc;
  6634. Emphasis_GetRect( rcWork, rc );
  6635. int height = rc.bottom - rc.top;
  6636. int dx = x - m_nLastX;
  6637. int dy = y - m_nLastY;
  6638. float dfdx = (float)dx * GetTimePerPixel();
  6639. float dfdy = (float)dy / (float)height;
  6640. for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
  6641. {
  6642. CEmphasisSample *sample = m_Tags.GetSample( i );
  6643. if ( !sample || !sample->selected )
  6644. continue;
  6645. sample->time += dfdx;
  6646. //sample->time = clamp( sample->time, 0.0f, 1.0f );
  6647. sample->value -= dfdy;
  6648. sample->value = clamp( sample->value, 0.0f, 1.0f );
  6649. }
  6650. }
  6651. void PhonemeEditor::Emphasis_Redraw( CChoreoWidgetDrawHelper& drawHelper, RECT& rcWorkSpace )
  6652. {
  6653. if ( GetMode() != MODE_EMPHASIS &&
  6654. GetMode() != MODE_PHONEMES )
  6655. return;
  6656. bool fullmode = GetMode() == MODE_EMPHASIS;
  6657. RECT rcClient;
  6658. Emphasis_GetRect( rcWorkSpace, rcClient );
  6659. RECT rcText;
  6660. rcText = rcClient;
  6661. InflateRect( &rcText, -15, 0 );
  6662. OffsetRect( &rcText, 0, -20 );
  6663. rcText.bottom = rcText.top + 20;
  6664. if ( fullmode )
  6665. {
  6666. drawHelper.DrawColoredText( "Arial", 15, FW_BOLD, PEColor( COLOR_PHONEME_EMPHASIS_TEXT ), rcText, "Emphasis..." );
  6667. }
  6668. {
  6669. int h = rcClient.bottom - rcClient.top;
  6670. int offset = h/3;
  6671. RECT rcSpot = rcClient;
  6672. rcSpot.bottom = rcSpot.top + offset;
  6673. drawHelper.DrawGradientFilledRect(
  6674. rcSpot,
  6675. PEColor( COLOR_PHONEME_EMPHASIS_BG_STRONG ),
  6676. PEColor( COLOR_PHONEME_EMPHASIS_BG ),
  6677. true );
  6678. OffsetRect( &rcSpot, 0, offset );
  6679. drawHelper.DrawFilledRect( PEColor( COLOR_PHONEME_EMPHASIS_BG ), rcSpot );
  6680. OffsetRect( &rcSpot, 0, offset );
  6681. drawHelper.DrawGradientFilledRect(
  6682. rcSpot,
  6683. PEColor( COLOR_PHONEME_EMPHASIS_BG ),
  6684. PEColor( COLOR_PHONEME_EMPHASIS_BG_WEAK ),
  6685. true );
  6686. }
  6687. Color gray = PEColor( COLOR_PHONEME_EMPHASIS_MIDLINE );
  6688. drawHelper.DrawOutlinedRect( PEColor( COLOR_PHONEME_EMPHASIS_BORDER ), PS_SOLID, 1, rcClient );
  6689. Color lineColor = PEColor( COLOR_PHONEME_EMPHASIS_LINECOLOR );
  6690. Color dotColor = PEColor( COLOR_PHONEME_EMPHASIS_DOTCOLOR );
  6691. Color dotColorSelected = PEColor( COLOR_PHONEME_EMPHASIS_DOTCOLOR_SELECTED );
  6692. int midy = ( rcClient.bottom + rcClient.top ) / 2;
  6693. drawHelper.DrawColoredLine( gray, PS_SOLID, 1, rcClient.left, midy,
  6694. rcClient.right, midy );
  6695. int height = rcClient.bottom - rcClient.top;
  6696. int bottom = rcClient.bottom - 1;
  6697. if ( !m_pWaveFile )
  6698. return;
  6699. float running_length = m_pWaveFile->GetRunningLength();
  6700. // FIXME: adjust this based on framerate....
  6701. float timeperpixel = GetTimePerPixel();
  6702. float starttime, endtime;
  6703. GetScreenStartAndEndTime( starttime, endtime );
  6704. int prevx = 0;
  6705. float prev_t = starttime;
  6706. float prev_value = m_Tags.GetIntensity( prev_t, running_length );
  6707. int dx = 5;
  6708. for ( int x = 0; x < ( w2() + dx ); x += dx )
  6709. {
  6710. float t = GetTimeForPixel( x );
  6711. float value = m_Tags.GetIntensity( t, running_length );
  6712. // Draw segment
  6713. drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1,
  6714. prevx,
  6715. bottom - prev_value * height,
  6716. x,
  6717. bottom - value * height );
  6718. prev_t = t;
  6719. prev_value = value;
  6720. prevx = x;
  6721. }
  6722. int numsamples = m_Tags.GetNumSamples();
  6723. for ( int sample = 0; sample < numsamples; sample++ )
  6724. {
  6725. CEmphasisSample *start = m_Tags.GetSample( sample );
  6726. int x = ( start->time - starttime ) / timeperpixel;
  6727. float value = m_Tags.GetIntensity( start->time, running_length );
  6728. int y = bottom - value * height;
  6729. int dotsize = 4;
  6730. int dotSizeSelected = 5;
  6731. Color clr = dotColor;
  6732. Color clrSelected = dotColorSelected;
  6733. drawHelper.DrawCircle(
  6734. start->selected ? clrSelected : clr,
  6735. x, y,
  6736. start->selected ? dotSizeSelected : dotsize,
  6737. true );
  6738. }
  6739. }
  6740. //-----------------------------------------------------------------------------
  6741. // Purpose:
  6742. // Output : Returns true on success, false on failure.
  6743. //-----------------------------------------------------------------------------
  6744. bool PhonemeEditor::Emphasis_IsValid( void )
  6745. {
  6746. if ( m_Tags.GetNumSamples() > 0 )
  6747. return true;
  6748. return false;
  6749. }
  6750. //-----------------------------------------------------------------------------
  6751. // Purpose:
  6752. //-----------------------------------------------------------------------------
  6753. void PhonemeEditor::Emphasis_SelectPoints( void )
  6754. {
  6755. if ( GetMode() != MODE_EMPHASIS )
  6756. return;
  6757. RECT rcWork, rcEmphasis;
  6758. GetWorkspaceRect( rcWork );
  6759. Emphasis_GetRect( rcWork, rcEmphasis );
  6760. RECT rcSelection;
  6761. rcSelection.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX;
  6762. rcSelection.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX;
  6763. rcSelection.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY;
  6764. rcSelection.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY;
  6765. rcSelection.top = max( rcSelection.top, rcEmphasis.top );
  6766. rcSelection.bottom = min( rcSelection.bottom, rcEmphasis.bottom );
  6767. int eh, ew;
  6768. eh = rcEmphasis.bottom - rcEmphasis.top;
  6769. ew = rcEmphasis.right - rcEmphasis.left;
  6770. InflateRect( &rcSelection, 5, 5 );
  6771. if ( !w2() || !h2() )
  6772. return;
  6773. float fleft = GetTimeForPixel( rcSelection.left );
  6774. float fright = GetTimeForPixel( rcSelection.right );
  6775. float ftop = (float)( rcSelection.top - rcEmphasis.top ) / (float)eh;
  6776. float fbottom = (float)( rcSelection.bottom- rcEmphasis.top ) / (float)eh;
  6777. //fleft = clamp( fleft, 0.0f, 1.0f );
  6778. //fright = clamp( fright, 0.0f, 1.0f );
  6779. ftop = clamp( ftop, 0.0f, 1.0f );
  6780. fbottom = clamp( fbottom, 0.0f, 1.0f );
  6781. float eps = 0.005;
  6782. for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
  6783. {
  6784. CEmphasisSample *sample = m_Tags.GetSample( i );
  6785. if ( sample->time + eps < fleft )
  6786. continue;
  6787. if ( sample->time - eps > fright )
  6788. continue;
  6789. if ( (1.0f - sample->value ) + eps < ftop )
  6790. continue;
  6791. if ( (1.0f - sample->value ) - eps > fbottom )
  6792. continue;
  6793. sample->selected = true;
  6794. }
  6795. redraw();
  6796. }
  6797. //-----------------------------------------------------------------------------
  6798. // Purpose:
  6799. // Input : rcHandle -
  6800. //-----------------------------------------------------------------------------
  6801. void PhonemeEditor::GetScrubHandleRect( RECT& rcHandle, bool clipped )
  6802. {
  6803. float pixel = 0.0f;
  6804. if ( m_pWaveFile )
  6805. {
  6806. float currenttime = m_flScrub;
  6807. float starttime, endtime;
  6808. GetScreenStartAndEndTime( starttime, endtime );
  6809. float screenfrac = ( currenttime - starttime ) / ( endtime - starttime );
  6810. pixel = screenfrac * w2();
  6811. if ( clipped )
  6812. {
  6813. pixel = clamp( pixel, SCRUBBER_HANDLE_WIDTH/2, w2() - SCRUBBER_HANDLE_WIDTH/2 );
  6814. }
  6815. }
  6816. rcHandle.left = pixel-SCRUBBER_HANDLE_WIDTH/2;
  6817. rcHandle.right = pixel + SCRUBBER_HANDLE_WIDTH/2;
  6818. rcHandle.top = 2 + GetCaptionHeight() + 12;
  6819. rcHandle.bottom = rcHandle.top + SCRUBBER_HANDLE_HEIGHT;
  6820. }
  6821. //-----------------------------------------------------------------------------
  6822. // Purpose:
  6823. // Input : rcArea -
  6824. //-----------------------------------------------------------------------------
  6825. void PhonemeEditor::GetScrubAreaRect( RECT& rcArea )
  6826. {
  6827. rcArea.left = 0;
  6828. rcArea.right = w2();
  6829. rcArea.top = 2 + GetCaptionHeight() + 12;
  6830. rcArea.bottom = rcArea.top + SCRUBBER_HEIGHT - 4;
  6831. }
  6832. void PhonemeEditor::DrawScrubHandle()
  6833. {
  6834. RECT rcHandle;
  6835. GetScrubHandleRect( rcHandle, true );
  6836. rcHandle.left = 0;
  6837. rcHandle.right = w2();
  6838. CChoreoWidgetDrawHelper drawHelper( this, rcHandle );
  6839. DrawScrubHandle( drawHelper );
  6840. }
  6841. //-----------------------------------------------------------------------------
  6842. // Purpose:
  6843. // Input : drawHelper -
  6844. // rcHandle -
  6845. //-----------------------------------------------------------------------------
  6846. void PhonemeEditor::DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper )
  6847. {
  6848. RECT rcHandle;
  6849. GetScrubHandleRect( rcHandle, true );
  6850. HBRUSH br = CreateSolidBrush( ColorToRGB( Color( 0, 150, 100 ) ) );
  6851. Color areaBorder = Color( 230, 230, 220 );
  6852. drawHelper.DrawColoredLine( areaBorder,
  6853. PS_SOLID, 1, 0, rcHandle.top, w2(), rcHandle.top );
  6854. drawHelper.DrawColoredLine( areaBorder,
  6855. PS_SOLID, 1, 0, rcHandle.bottom, w2(), rcHandle.bottom );
  6856. drawHelper.DrawFilledRect( br, rcHandle );
  6857. //
  6858. char sz[ 32 ];
  6859. sprintf( sz, "%.3f", m_flScrub );
  6860. int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
  6861. RECT rcText = rcHandle;
  6862. int textw = rcText.right - rcText.left;
  6863. rcText.left += ( textw - len ) / 2;
  6864. drawHelper.DrawColoredText( "Arial", 9, 500, Color( 255, 255, 255 ), rcText, sz );
  6865. DeleteObject( br );
  6866. }
  6867. //-----------------------------------------------------------------------------
  6868. // Purpose:
  6869. // Input : *event -
  6870. // Output : Returns true on success, false on failure.
  6871. //-----------------------------------------------------------------------------
  6872. bool PhonemeEditor::IsMouseOverScrubHandle( mxEvent *event )
  6873. {
  6874. RECT rcHandle;
  6875. GetScrubHandleRect( rcHandle, true );
  6876. InflateRect( &rcHandle, 2, 2 );
  6877. POINT pt;
  6878. pt.x = (short)event->x;
  6879. pt.y = (short)event->y;
  6880. if ( PtInRect( &rcHandle, pt ) )
  6881. {
  6882. return true;
  6883. }
  6884. return false;
  6885. }
  6886. bool PhonemeEditor::IsMouseOverScrubArea( mxEvent *event )
  6887. {
  6888. RECT rcArea;
  6889. rcArea.left = 0;
  6890. rcArea.right = w2();
  6891. rcArea.top = 2 + GetCaptionHeight() + 12;
  6892. rcArea.bottom = rcArea.top + 10;
  6893. InflateRect( &rcArea, 2, 2 );
  6894. POINT pt;
  6895. pt.x = (short)event->x;
  6896. pt.y = (short)event->y;
  6897. if ( PtInRect( &rcArea, pt ) )
  6898. {
  6899. return true;
  6900. }
  6901. return false;
  6902. }
  6903. float PhonemeEditor::GetTimeForSample( int sample )
  6904. {
  6905. if ( !m_pWaveFile )
  6906. {
  6907. return 0.0f;
  6908. }
  6909. float duration = m_pWaveFile->GetRunningLength();
  6910. int sampleCount = m_pWaveFile->SampleCount();
  6911. if ( sampleCount <= 0 )
  6912. return 0.0f;
  6913. float frac = (float)sample / (float)sampleCount;
  6914. return frac * duration;
  6915. }
  6916. void PhonemeEditor::ClampTimeToSelectionInterval( float& timeval )
  6917. {
  6918. if ( !m_pWaveFile )
  6919. {
  6920. return;
  6921. }
  6922. if ( !m_pMixer || !sound->IsSoundPlaying( m_pMixer ) )
  6923. {
  6924. return;
  6925. }
  6926. if ( !m_bSelectionActive )
  6927. return;
  6928. float starttime = GetTimeForSample( m_nSelection[ 0 ] );
  6929. float endtime = GetTimeForSample( m_nSelection[ 1 ] );
  6930. Assert( starttime <= endtime );
  6931. timeval = clamp( timeval, starttime, endtime );
  6932. }
  6933. void PhonemeEditor::ScrubThink( float dt, bool scrubbing )
  6934. {
  6935. ClampTimeToSelectionInterval( m_flScrub );
  6936. ClampTimeToSelectionInterval( m_flScrubTarget );
  6937. if ( m_flScrubTarget == m_flScrub && !scrubbing )
  6938. {
  6939. if ( sound->IsSoundPlaying( m_pMixer ) )
  6940. {
  6941. sound->StopSound( m_pMixer );
  6942. }
  6943. return;
  6944. }
  6945. if ( !m_pWaveFile )
  6946. return;
  6947. bool newmixer = false;
  6948. if ( !m_pMixer || !sound->IsSoundPlaying( m_pMixer ) )
  6949. {
  6950. m_pMixer = NULL;
  6951. SaveLinguisticData();
  6952. StudioModel *model = NULL;//models->GetActiveStudioModel();
  6953. sound->PlaySound( model, VOL_NORM, m_WorkFile.m_szWorkingFile, &m_pMixer );
  6954. newmixer = true;
  6955. }
  6956. if ( !m_pMixer )
  6957. {
  6958. return;
  6959. }
  6960. if ( m_flScrub > m_flScrubTarget )
  6961. {
  6962. m_pMixer->SetDirection( false );
  6963. }
  6964. else
  6965. {
  6966. m_pMixer->SetDirection( true );
  6967. }
  6968. float duration = m_pWaveFile->GetRunningLength();
  6969. if ( !duration )
  6970. return;
  6971. float d = m_flScrubTarget - m_flScrub;
  6972. int sign = d > 0.0f ? 1 : -1;
  6973. float maxmove = dt * m_flPlaybackRate;
  6974. if ( sign > 0 )
  6975. {
  6976. if ( d < maxmove )
  6977. {
  6978. m_flScrub = m_flScrubTarget;
  6979. }
  6980. else
  6981. {
  6982. m_flScrub += maxmove;
  6983. }
  6984. }
  6985. else
  6986. {
  6987. if ( -d < maxmove )
  6988. {
  6989. m_flScrub = m_flScrubTarget;
  6990. }
  6991. else
  6992. {
  6993. m_flScrub -= maxmove;
  6994. }
  6995. }
  6996. int sampleCount = m_pMixer->GetSource()->SampleCount();
  6997. int cursample = sampleCount * ( m_flScrub / duration );
  6998. int realsample = m_pMixer->GetSamplePosition();
  6999. int dsample = cursample - realsample;
  7000. int onehundredth = m_pMixer->GetSource()->SampleRate() * 0.01f;
  7001. if ( abs( dsample ) > onehundredth )
  7002. {
  7003. m_pMixer->SetSamplePosition( cursample, true );
  7004. }
  7005. m_pMixer->SetActive( true );
  7006. RECT rcArea;
  7007. GetScrubAreaRect( rcArea );
  7008. CChoreoWidgetDrawHelper drawHelper( this, rcArea );
  7009. DrawScrubHandle( drawHelper );
  7010. if ( scrubbing )
  7011. {
  7012. g_pMatSysWindow->Frame();
  7013. }
  7014. }
  7015. void PhonemeEditor::SetScrubTime( float t )
  7016. {
  7017. m_flScrub = t;
  7018. ClampTimeToSelectionInterval( m_flScrub );
  7019. }
  7020. void PhonemeEditor::SetScrubTargetTime( float t )
  7021. {
  7022. m_flScrubTarget = t;
  7023. ClampTimeToSelectionInterval( m_flScrubTarget );
  7024. }
  7025. void PhonemeEditor::OnToggleVoiceDuck()
  7026. {
  7027. SetDirty( true );
  7028. PushUndo();
  7029. m_Tags.SetVoiceDuck( !m_Tags.GetVoiceDuck() );
  7030. PushRedo();
  7031. redraw();
  7032. }
  7033. void PhonemeEditor::Play()
  7034. {
  7035. PlayEditedWave( m_bSelectionActive );
  7036. }