Team Fortress 2 Source Code as on 22/4/2020
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.

8799 lines
190 KiB

  1. //========= Copyright 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. COLORREF root_color;
  67. COLORREF gray_color; // if mode is wrong...
  68. };
  69. static PhonemeEditorColor g_PEColors[ NUM_COLORS ] =
  70. {
  71. { COLOR_PHONEME_BACKGROUND, -1, RGB( 240, 240, 220 ) },
  72. { COLOR_PHONEME_TEXT, -1, RGB( 63, 63, 63 ) },
  73. { COLOR_PHONEME_LIGHTTEXT, 0, RGB( 180, 180, 120 ) },
  74. { COLOR_PHONEME_PLAYBACKTICK, 0, RGB( 255, 0, 0 ) },
  75. { COLOR_PHONEME_WAVDATA, 0, RGB( 128, 31, 63 ) },
  76. { COLOR_PHONEME_TIMELINE, 0, RGB( 31, 31, 127 ) },
  77. { COLOR_PHONEME_TIMELINE_MAJORTICK, 0, RGB( 200, 200, 255 ) },
  78. { COLOR_PHONEME_TIMELINE_MINORTICK, 0, RGB( 210, 210, 240 ) },
  79. { COLOR_PHONEME_EXTRACTION_RESULT_FAIL, 0, RGB( 180, 180, 0 ) },
  80. { COLOR_PHONEME_EXTRACTION_RESULT_SUCCESS, 0, RGB( 100, 180, 100 ) },
  81. { COLOR_PHONEME_EXTRACTION_RESULT_ERROR, 0, RGB( 255, 31, 31 ) },
  82. { COLOR_PHONEME_EXTRACTION_RESULT_OTHER, 0, RGB( 63, 63, 63 ) },
  83. { COLOR_PHONEME_TAG_BORDER, 0, RGB( 160, 100, 100 ) },
  84. { COLOR_PHONEME_TAG_BORDER_SELECTED, 0, RGB( 255, 40, 60 ) },
  85. { COLOR_PHONEME_TAG_FILLER_NORMAL, 0, RGB( 210, 210, 190 ) },
  86. { COLOR_PHONEME_TAG_SELECTED, 0, RGB( 200, 130, 130 ) },
  87. { COLOR_PHONEME_TAG_TEXT, 0, RGB( 63, 63, 63 ) },
  88. { COLOR_PHONEME_TAG_TEXT_SELECTED, 0, RGB( 250, 250, 250 ) },
  89. { COLOR_PHONEME_WAV_ENDPOINT, 0, RGB( 0, 0, 200 ) },
  90. { COLOR_PHONEME_AB, 0, RGB( 63, 190, 210 ) },
  91. { COLOR_PHONEME_AB_LINE, 0, RGB( 31, 150, 180 ) },
  92. { COLOR_PHONEME_AB_TEXT, 0, RGB( 100, 120, 120 ) },
  93. { COLOR_PHONEME_ACTIVE_BORDER, 0, RGB( 150, 240, 180 ) },
  94. { COLOR_PHONEME_SELECTED_BORDER, 0, RGB( 255, 0, 0 ) },
  95. { COLOR_PHONEME_TIMING_TAG, -1, RGB( 0, 100, 200 ) },
  96. { COLOR_PHONEME_EMPHASIS_BG, 1, RGB( 230, 230, 200 ) },
  97. { COLOR_PHONEME_EMPHASIS_BG_STRONG, 1, RGB( 163, 201, 239 ) },
  98. { COLOR_PHONEME_EMPHASIS_BG_WEAK, 1, RGB( 237, 239, 163 ) },
  99. { COLOR_PHONEME_EMPHASIS_BORDER, 1, RGB( 200, 200, 200 ) },
  100. { COLOR_PHONEME_EMPHASIS_LINECOLOR, 1, RGB( 0, 0, 255 ) },
  101. { COLOR_PHONEME_EMPHASIS_DOTCOLOR, 1, RGB( 0, 0, 255 ) },
  102. { COLOR_PHONEME_EMPHASIS_DOTCOLOR_SELECTED, 1, RGB( 240, 80, 20 ) },
  103. { COLOR_PHONEME_EMPHASIS_TEXT, 1, RGB( 0, 0, 0 ) },
  104. { COLOR_PHONEME_EMPHASIS_MIDLINE, 1, RGB( 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. int open( const char *pFileName )
  129. {
  130. return (int)filesystem->Open( pFileName, "rb" );
  131. }
  132. int read( void *pOutput, int size, int file )
  133. {
  134. if ( !file )
  135. return 0;
  136. return filesystem->Read( pOutput, size, (FileHandle_t)file );
  137. }
  138. void seek( int file, int pos )
  139. {
  140. if ( !file )
  141. return;
  142. filesystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD );
  143. }
  144. unsigned int tell( int file )
  145. {
  146. if ( !file )
  147. return 0;
  148. return filesystem->Tell( (FileHandle_t)file );
  149. }
  150. unsigned int size( int file )
  151. {
  152. if ( !file )
  153. return 0;
  154. return filesystem->Size( (FileHandle_t)file );
  155. }
  156. void close( int file )
  157. {
  158. if ( !file )
  159. return;
  160. filesystem->Close( (FileHandle_t)file );
  161. }
  162. };
  163. class StdIOWriteBinary : public IFileWriteBinary
  164. {
  165. public:
  166. int create( const char *pFileName )
  167. {
  168. MakeFileWriteable( pFileName );
  169. return (int)filesystem->Open( pFileName, "wb" );
  170. }
  171. int write( void *pData, int size, int file )
  172. {
  173. return filesystem->Write( pData, size, (FileHandle_t)file );
  174. }
  175. void close( int file )
  176. {
  177. filesystem->Close( (FileHandle_t)file );
  178. }
  179. void seek( int file, int pos )
  180. {
  181. filesystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD );
  182. }
  183. unsigned int tell( int file )
  184. {
  185. return filesystem->Tell( (FileHandle_t)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. COLORREF bgColor = g_PEColors[ COLOR_PHONEME_BACKGROUND ].root_color;
  328. int bgr, bgg, bgb;
  329. bgr = GetRValue( bgColor );
  330. bgg = GetGValue( bgColor );
  331. bgb = GetBValue( bgColor );
  332. int r, g, b;
  333. r = GetRValue( p->root_color );
  334. g = GetGValue( p->root_color );
  335. b = GetBValue( p->root_color );
  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 = RGB( avg, avg, avg );
  341. }
  342. }
  343. }
  344. COLORREF PhonemeEditor::PEColor( int colornum )
  345. {
  346. COLORREF clr = RGB( 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. V_strcpy_safe( 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. V_strcpy_safe( 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.Size() <= 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.Size(); 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.Size(); i++ )
  1243. {
  1244. CWordTag *word = m_Tags.m_Words[ i ];
  1245. if ( !word )
  1246. continue;
  1247. for ( int j = 0; j < word->m_Phonemes.Size(); 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.Size(); 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.Size() && !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. COLORREF 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( (LONG)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.Size(); 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.Size(); 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. COLORREF 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( (LONG)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( ( 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( 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. COLORREF 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.Size(), 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.Size() );
  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.Size() > 0 )
  1848. {
  1849. int length = drawHelper.CalcTextWidth( font, fontsize, fontweight,
  1850. "Undo levels: %i/%i", m_nUndoLevel, m_UndoStack.Size() );
  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.Size() );
  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.Size(); i++ )
  2139. {
  2140. CWordTag *word = m_Tags.m_Words[ i ];
  2141. for ( int k = 0; k < word->m_Phonemes.Size(); 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.Size(); 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.Size(); 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.Size() == 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.Size() > 0 )
  2275. {
  2276. pop->addSeparator();
  2277. pop->add( va( "Cleanup words/phonemes" ), IDC_CLEANUP );
  2278. }
  2279. if ( m_Tags.m_Words.Size() > 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.Size() > 0 )
  2355. {
  2356. pop->addSeparator();
  2357. pop->add( va( "Cleanup words/phonemes" ), IDC_CLEANUP );
  2358. }
  2359. if ( m_Tags.m_Words.Size() > 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.Size(); w++ )
  2395. {
  2396. CWordTag *word = m_Tags.m_Words[ w ];
  2397. for ( int i = 0; i < word->m_Phonemes.Size(); 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.Size() > 0 )
  2434. {
  2435. // Check last word, but only if it has some phonemes
  2436. CWordTag *lastWord = m_Tags.m_Words[ m_Tags.m_Words.Size() - 1 ];
  2437. if ( lastWord &&
  2438. ( lastWord->m_Phonemes.Size() > 0 ) )
  2439. {
  2440. CPhonemeTag *last = lastWord->m_Phonemes[ lastWord->m_Phonemes.Size() - 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.Size(); 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.Size() > 0 )
  2503. {
  2504. CWordTag *last = m_Tags.m_Words[ m_Tags.m_Words.Size() - 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.Size(); 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.Size(); w++ )
  2579. {
  2580. CWordTag *word = m_Tags.m_Words[ w ];
  2581. for ( int i = 0; i < word->m_Phonemes.Size(); 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.Size() - 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.Size() - 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.Size() - 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.Size() == 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.Size(); 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. szCroppedFile,
  3239. m_nSelection[ 0 ],
  3240. m_nSelection[ 1 ] );
  3241. return;
  3242. }
  3243. // Save any pending stuff
  3244. SaveLinguisticData();
  3245. // Store off copy of complete sentence
  3246. m_TagsExt = m_Tags;
  3247. char filename[ 512 ];
  3248. Q_snprintf( filename, sizeof( filename ), "%s%s", m_WorkFile.m_szBasePath, szCroppedFile );
  3249. m_nLastExtractionResult = m_pPhonemeExtractor->Extract(
  3250. filename,
  3251. (int)( m_pCroppedWave->GetRunningLength() * m_pCroppedWave->SampleRate() * m_pCroppedWave->TrueSampleSize() ),
  3252. Con_Printf,
  3253. m_InputWords,
  3254. m_Results );
  3255. if ( m_InputWords.m_Words.Size() != m_Results.m_Words.Size() )
  3256. {
  3257. Con_Printf( "Extraction returned %i words, source had %i, try adjusting selection\n",
  3258. m_Results.m_Words.Size(), m_InputWords.m_Words.Size() );
  3259. filesystem->RemoveFile( filename, "GAME" );
  3260. redraw();
  3261. return;
  3262. }
  3263. float bytespersecond = m_pCroppedWave->SampleRate() * m_pCroppedWave->TrueSampleSize();
  3264. // Tracker 57389:
  3265. // Total hack to fix a bug where the Lipsinc extractor is messing up the # channels on 16 bit stereo waves
  3266. if ( m_pPhonemeExtractor->GetAPIType() == SPEECH_API_LIPSINC &&
  3267. m_pCroppedWave->IsStereoWav() &&
  3268. m_pCroppedWave->SampleSize() == 16 )
  3269. {
  3270. bytespersecond *= 2.0f;
  3271. }
  3272. // Now convert byte offsets to times
  3273. for ( i = 0; i < m_Results.m_Words.Size(); i++ )
  3274. {
  3275. CWordTag *tag = m_Results.m_Words[ i ];
  3276. Assert( tag );
  3277. if ( !tag )
  3278. continue;
  3279. tag->m_flStartTime = ( float )(tag->m_uiStartByte ) / bytespersecond;
  3280. tag->m_flEndTime = ( float )(tag->m_uiEndByte ) / bytespersecond;
  3281. for ( int j = 0; j < tag->m_Phonemes.Size(); j++ )
  3282. {
  3283. CPhonemeTag *ptag = tag->m_Phonemes[ j ];
  3284. Assert( ptag );
  3285. if ( !ptag )
  3286. continue;
  3287. ptag->SetStartTime( ( float )(ptag->m_uiStartByte ) / bytespersecond );
  3288. ptag->SetEndTime( ( float )(ptag->m_uiEndByte ) / bytespersecond );
  3289. }
  3290. }
  3291. if ( usingselection )
  3292. {
  3293. // Copy data into m_TagsExt, offseting times by selectionstarttime
  3294. CWordTag *from;
  3295. CWordTag *to;
  3296. int fromWord = 0;
  3297. for ( i = 0; i < m_TagsExt.m_Words.Size() ; i++ )
  3298. {
  3299. to = m_TagsExt.m_Words[ i ];
  3300. if ( !to || !to->m_bSelected )
  3301. continue;
  3302. // Found start of contiguous run
  3303. if ( fromWord >= m_Results.m_Words.Size() )
  3304. break;
  3305. from = m_Results.m_Words[ fromWord++ ];
  3306. Assert( from );
  3307. if ( !from )
  3308. continue;
  3309. // Remove all phonemes from destination
  3310. while ( to->m_Phonemes.Size() > 0 )
  3311. {
  3312. CPhonemeTag *p = to->m_Phonemes[ 0 ];
  3313. Assert( p );
  3314. to->m_Phonemes.Remove( 0 );
  3315. delete p;
  3316. }
  3317. // Now copy phonemes from source
  3318. for ( int j = 0; j < from->m_Phonemes.Size(); j++ )
  3319. {
  3320. CPhonemeTag *fromPhoneme = from->m_Phonemes[ j ];
  3321. Assert( fromPhoneme );
  3322. if ( !fromPhoneme )
  3323. continue;
  3324. CPhonemeTag newPhoneme( *fromPhoneme );
  3325. // Offset start time
  3326. newPhoneme.AddStartTime( selectionstarttime );
  3327. newPhoneme.AddEndTime( selectionstarttime );
  3328. // Add it back in with corrected timing data
  3329. CPhonemeTag *p = new CPhonemeTag( newPhoneme );
  3330. Assert( p );
  3331. if ( p )
  3332. {
  3333. to->m_Phonemes.AddToTail( p );
  3334. }
  3335. }
  3336. // Done
  3337. if ( fromWord >= m_Results.m_Words.Size() )
  3338. break;
  3339. }
  3340. }
  3341. else
  3342. {
  3343. // Find word just before starting point of selection and
  3344. // place input words into list starting that that point
  3345. int startWord = 0;
  3346. CWordTag *firstWordOfPhrase = m_Results.m_Words[ 0 ];
  3347. Assert( firstWordOfPhrase );
  3348. for ( ; startWord < m_TagsExt.m_Words.Size(); startWord++ )
  3349. {
  3350. CWordTag *w = m_TagsExt.m_Words[ startWord ];
  3351. Assert( w );
  3352. if ( !w )
  3353. continue;
  3354. if ( w->m_flStartTime > firstWordOfPhrase->m_flStartTime + selectionstarttime )
  3355. break;
  3356. }
  3357. for ( i = 0; i < m_Results.m_Words.Size(); i++ )
  3358. {
  3359. CWordTag *from = m_Results.m_Words[ i ];
  3360. Assert( from );
  3361. if ( !from )
  3362. continue;
  3363. CWordTag *to = new CWordTag( *from );
  3364. Assert( to );
  3365. to->m_flStartTime += selectionstarttime;
  3366. to->m_flEndTime += selectionstarttime;
  3367. // Now adjust phoneme times
  3368. for ( int j = 0; j < to->m_Phonemes.Size(); j++ )
  3369. {
  3370. CPhonemeTag *toPhoneme = to->m_Phonemes[ j ];
  3371. Assert( toPhoneme );
  3372. if ( !toPhoneme )
  3373. continue;
  3374. // Offset start time
  3375. toPhoneme->AddStartTime( selectionstarttime );
  3376. toPhoneme->AddEndTime( selectionstarttime );
  3377. }
  3378. m_TagsExt.m_Words.InsertBefore( startWord++, to );
  3379. }
  3380. }
  3381. Con_Printf( "Cleaning up...\n" );
  3382. filesystem->RemoveFile( filename, "GAME" );
  3383. SetFocus( (HWND)getHandle() );
  3384. redraw();
  3385. }
  3386. void PhonemeEditor::RedoPhonemeExtraction( void )
  3387. {
  3388. if ( GetMode() != MODE_PHONEMES )
  3389. return;
  3390. if ( !CheckSpeechAPI() )
  3391. return;
  3392. m_nLastExtractionResult = SR_RESULT_NORESULT;
  3393. if ( !m_pWaveFile )
  3394. return;
  3395. SaveLinguisticData();
  3396. // Send m_WorkFile.m_szWorkingFile to extractor and retrieve resulting data
  3397. //
  3398. m_TagsExt.Reset();
  3399. Assert( m_pPhonemeExtractor );
  3400. char filename[ 512 ];
  3401. Q_snprintf( filename, sizeof( filename ), "%s%s", m_WorkFile.m_szBasePath, m_WorkFile.m_szWorkingFile );
  3402. m_nLastExtractionResult = m_pPhonemeExtractor->Extract(
  3403. filename,
  3404. (int)( m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate() * m_pWaveFile->TrueSampleSize() ),
  3405. Con_Printf,
  3406. m_Tags,
  3407. m_TagsExt );
  3408. float bytespersecond = m_pWaveFile->SampleRate() * m_pWaveFile->TrueSampleSize();
  3409. // Now convert byte offsets to times
  3410. int i;
  3411. for ( i = 0; i < m_TagsExt.m_Words.Size(); i++ )
  3412. {
  3413. CWordTag *tag = m_TagsExt.m_Words[ i ];
  3414. Assert( tag );
  3415. if ( !tag )
  3416. continue;
  3417. tag->m_flStartTime = ( float )(tag->m_uiStartByte ) / bytespersecond;
  3418. tag->m_flEndTime = ( float )(tag->m_uiEndByte ) / bytespersecond;
  3419. for ( int j = 0; j < tag->m_Phonemes.Size(); j++ )
  3420. {
  3421. CPhonemeTag *ptag = tag->m_Phonemes[ j ];
  3422. Assert( ptag );
  3423. if ( !ptag )
  3424. continue;
  3425. ptag->SetStartTime( ( float )(ptag->m_uiStartByte ) / bytespersecond );
  3426. ptag->SetEndTime( ( float )(ptag->m_uiEndByte ) / bytespersecond );
  3427. }
  3428. }
  3429. SetFocus( (HWND)getHandle() );
  3430. redraw();
  3431. }
  3432. //-----------------------------------------------------------------------------
  3433. // Purpose:
  3434. //-----------------------------------------------------------------------------
  3435. void PhonemeEditor::Deselect( void )
  3436. {
  3437. m_nSelection[ 0 ] = m_nSelection[ 1 ] = 0;
  3438. m_bSelectionActive = false;
  3439. }
  3440. void PhonemeEditor::ITER_SelectSpanningWords( CWordTag *word, float amount )
  3441. {
  3442. Assert( word );
  3443. word->m_bSelected = false;
  3444. if ( !m_bSelectionActive )
  3445. return;
  3446. if ( !m_pWaveFile )
  3447. return;
  3448. float numsamples = m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate();
  3449. if ( numsamples > 0.0f )
  3450. {
  3451. // Convert sample #'s to time
  3452. float starttime = ( m_nSelection[ 0 ] / numsamples ) * m_pWaveFile->GetRunningLength();
  3453. float endtime = ( m_nSelection[ 1 ] / numsamples ) * m_pWaveFile->GetRunningLength();
  3454. if ( word->m_flEndTime >= starttime &&
  3455. word->m_flStartTime <= endtime )
  3456. {
  3457. word->m_bSelected = true;
  3458. m_bWordsActive = true;
  3459. }
  3460. }
  3461. }
  3462. //-----------------------------------------------------------------------------
  3463. // Purpose:
  3464. // Input : start -
  3465. // end -
  3466. //-----------------------------------------------------------------------------
  3467. void PhonemeEditor::SelectSamples( int start, int end )
  3468. {
  3469. if ( !m_pWaveFile )
  3470. return;
  3471. // Make sure order is correct
  3472. if ( end < start )
  3473. {
  3474. int temp = end;
  3475. end = start;
  3476. start = temp;
  3477. }
  3478. Deselect();
  3479. m_nSelection[ 0 ] = start;
  3480. m_nSelection[ 1 ] = end;
  3481. m_bSelectionActive = true;
  3482. // Select any words that span the selection
  3483. //
  3484. TraverseWords( &PhonemeEditor::ITER_SelectSpanningWords, 0.0f );
  3485. redraw();
  3486. }
  3487. void PhonemeEditor::FinishMoveSelection( int startx, int mx )
  3488. {
  3489. if ( !m_pWaveFile )
  3490. return;
  3491. int sampleStart = GetSampleForMouse( startx );
  3492. int sampleEnd = GetSampleForMouse( mx );
  3493. int delta = sampleEnd - sampleStart;
  3494. for ( int i = 0; i < 2; i++ )
  3495. {
  3496. m_nSelection[ i ] += delta;
  3497. }
  3498. // Select any words that span the selection
  3499. //
  3500. TraverseWords( &PhonemeEditor::ITER_SelectSpanningWords, 0.0f );
  3501. redraw();
  3502. }
  3503. void PhonemeEditor::FinishMoveSelectionStart( int startx, int mx )
  3504. {
  3505. if ( !m_pWaveFile )
  3506. return;
  3507. int sampleStart = GetSampleForMouse( startx );
  3508. int sampleEnd = GetSampleForMouse( mx );
  3509. int delta = sampleEnd - sampleStart;
  3510. m_nSelection[ 0 ] += delta;
  3511. if ( m_nSelection[ 0 ] >= m_nSelection[ 1 ] )
  3512. {
  3513. Deselect();
  3514. }
  3515. // Select any words that span the selection
  3516. //
  3517. TraverseWords( &PhonemeEditor::ITER_SelectSpanningWords, 0.0f );
  3518. redraw();
  3519. }
  3520. void PhonemeEditor::FinishMoveSelectionEnd( int startx, int mx )
  3521. {
  3522. if ( !m_pWaveFile )
  3523. return;
  3524. int sampleStart = GetSampleForMouse( startx );
  3525. int sampleEnd = GetSampleForMouse( mx );
  3526. int delta = sampleEnd - sampleStart;
  3527. m_nSelection[ 1 ] += delta;
  3528. if ( m_nSelection[ 1 ] <= m_nSelection[ 0 ] )
  3529. {
  3530. Deselect();
  3531. }
  3532. // Select any words that span the selection
  3533. //
  3534. TraverseWords( &PhonemeEditor::ITER_SelectSpanningWords, 0.0f );
  3535. redraw();
  3536. }
  3537. //-----------------------------------------------------------------------------
  3538. // Purpose:
  3539. // Input : startx -
  3540. // mx -
  3541. //-----------------------------------------------------------------------------
  3542. void PhonemeEditor::FinishSelect( int startx, int mx )
  3543. {
  3544. if ( !m_pWaveFile )
  3545. return;
  3546. // Don't select really small areas
  3547. if ( abs( startx - mx ) < 2 )
  3548. return;
  3549. int sampleStart = GetSampleForMouse( startx );
  3550. int sampleEnd = GetSampleForMouse( mx );
  3551. SelectSamples( sampleStart, sampleEnd );
  3552. }
  3553. //-----------------------------------------------------------------------------
  3554. // Purpose:
  3555. // Input : mx -
  3556. // my -
  3557. // Output : Returns true on success, false on failure.
  3558. //-----------------------------------------------------------------------------
  3559. bool PhonemeEditor::IsMouseOverSamples( int mx, int my )
  3560. {
  3561. if ( GetMode() != MODE_PHONEMES )
  3562. return false;
  3563. // Deterime if phoneme boundary is under the cursor
  3564. //
  3565. if ( !m_pWaveFile )
  3566. return false;
  3567. RECT rc;
  3568. GetWorkspaceRect( rc );
  3569. // Over tag
  3570. if ( my >= TAG_TOP && my <= TAG_BOTTOM )
  3571. return false;
  3572. if ( IsMouseOverPhonemeRow( my ) )
  3573. return false;
  3574. if ( IsMouseOverWordRow( my ) )
  3575. return false;
  3576. RECT rcWord;
  3577. GetWordTrayTopBottom( rcWord );
  3578. RECT rcPhoneme;
  3579. GetPhonemeTrayTopBottom( rcPhoneme );
  3580. if ( my < rcWord.bottom )
  3581. return false;
  3582. if ( my > rcPhoneme.top )
  3583. return false;
  3584. return true;
  3585. }
  3586. void PhonemeEditor::GetScreenStartAndEndTime( float &starttime, float& endtime )
  3587. {
  3588. starttime = m_nLeftOffset / GetPixelsPerSecond();
  3589. endtime = w2() / GetPixelsPerSecond() + starttime;
  3590. }
  3591. float PhonemeEditor::GetTimePerPixel( void )
  3592. {
  3593. RECT rc;
  3594. GetWorkspaceRect( rc );
  3595. float starttime, endtime;
  3596. GetScreenStartAndEndTime( starttime, endtime );
  3597. if ( rc.right - rc.left <= 0 )
  3598. {
  3599. return ( endtime - starttime );
  3600. }
  3601. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  3602. return timeperpixel;
  3603. }
  3604. int PhonemeEditor::GetPixelForSample( int sample )
  3605. {
  3606. RECT rc;
  3607. GetWorkspaceRect( rc );
  3608. if ( !m_pWaveFile )
  3609. return rc.left;
  3610. // Determine start/stop positions
  3611. int totalsamples = (int)( m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate() );
  3612. if ( totalsamples <= 0 )
  3613. {
  3614. return rc.left;
  3615. }
  3616. float starttime, endtime;
  3617. GetScreenStartAndEndTime( starttime, endtime );
  3618. float sampleFrac = (float)sample / (float)totalsamples;
  3619. float sampleTime = sampleFrac * (float)m_pWaveFile->GetRunningLength();
  3620. if ( endtime - starttime < 0.0f )
  3621. {
  3622. return rc.left;
  3623. }
  3624. float windowFrac = ( sampleTime - starttime ) / ( endtime - starttime );
  3625. return rc.left + (int)( windowFrac * ( rc.right - rc.left ) );
  3626. }
  3627. int PhonemeEditor::GetSampleForMouse( int mx )
  3628. {
  3629. if ( !m_pWaveFile )
  3630. return 0;
  3631. RECT rc;
  3632. GetWorkspaceRect( rc );
  3633. // Determine start/stop positions
  3634. int totalsamples = (int)( m_pWaveFile->GetRunningLength() * m_pWaveFile->SampleRate() );
  3635. float starttime, endtime;
  3636. GetScreenStartAndEndTime( starttime, endtime );
  3637. if ( GetPixelsPerSecond() <= 0 )
  3638. return 0;
  3639. // Start and end times
  3640. float clickTime = (float)mx / GetPixelsPerSecond() + starttime;
  3641. // What sample do these correspond to
  3642. if ( (float)m_pWaveFile->GetRunningLength() <= 0.0f )
  3643. return 0;
  3644. int sampleNumber = (int) ( (float)totalsamples * clickTime / (float)m_pWaveFile->GetRunningLength() );
  3645. return sampleNumber;
  3646. }
  3647. //-----------------------------------------------------------------------------
  3648. // Purpose:
  3649. // Input : mx -
  3650. // my -
  3651. // Output : Returns true on success, false on failure.
  3652. //-----------------------------------------------------------------------------
  3653. bool PhonemeEditor::IsMouseOverSelection( int mx, int my )
  3654. {
  3655. if ( GetMode() != MODE_PHONEMES )
  3656. return false;
  3657. if ( !m_pWaveFile )
  3658. return false;
  3659. if ( !m_bSelectionActive )
  3660. return false;
  3661. if ( !IsMouseOverSamples( mx, my ) )
  3662. return false;
  3663. int sampleNumber = GetSampleForMouse( mx );
  3664. if ( sampleNumber >= m_nSelection[ 0 ] - 3 &&
  3665. sampleNumber <= m_nSelection[ 1 ] + 3 )
  3666. {
  3667. return true;
  3668. }
  3669. return false;
  3670. }
  3671. bool PhonemeEditor::IsMouseOverSelectionStartEdge( mxEvent *event )
  3672. {
  3673. if ( GetMode() != MODE_PHONEMES )
  3674. return false;
  3675. if ( !m_pWaveFile )
  3676. return false;
  3677. int mx, my;
  3678. mx = (short)event->x;
  3679. my = (short)event->y;
  3680. if ( !(event->modifiers & mxEvent::KeyCtrl ) )
  3681. return false;
  3682. if ( !IsMouseOverSelection( mx, my ) )
  3683. return false;
  3684. int sample = GetSampleForMouse( mx );
  3685. int mouse_tolerance = 5;
  3686. RECT rc;
  3687. GetWorkspaceRect( rc );
  3688. // Determine start/stop positions
  3689. float timeperpixel = GetTimePerPixel();
  3690. int samplesperpixel = (int)( timeperpixel * m_pWaveFile->SampleRate() );
  3691. if ( abs( sample - m_nSelection[ 0 ] ) < mouse_tolerance * samplesperpixel )
  3692. {
  3693. return true;
  3694. }
  3695. return false;
  3696. }
  3697. bool PhonemeEditor::IsMouseOverSelectionEndEdge( mxEvent *event )
  3698. {
  3699. if ( GetMode() != MODE_PHONEMES )
  3700. return false;
  3701. if ( !m_pWaveFile )
  3702. return false;
  3703. int mx, my;
  3704. mx = (short)event->x;
  3705. my = (short)event->y;
  3706. if ( !(event->modifiers & mxEvent::KeyCtrl ) )
  3707. return false;
  3708. if ( !IsMouseOverSelection( mx, my ) )
  3709. return false;
  3710. int sample = GetSampleForMouse( mx );
  3711. int mouse_tolerance = 5;
  3712. RECT rc;
  3713. GetWorkspaceRect( rc );
  3714. if ( GetPixelsPerSecond() <= 0.0f )
  3715. return false;
  3716. if ( ( rc.right - rc.left ) <= 0 )
  3717. return false;
  3718. // Determine start/stop positions
  3719. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  3720. float endtime = w2() / GetPixelsPerSecond() + starttime;
  3721. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  3722. int samplesperpixel = (int)( timeperpixel * m_pWaveFile->SampleRate() );
  3723. if ( abs( sample - m_nSelection[ 1 ] ) < mouse_tolerance * samplesperpixel )
  3724. {
  3725. return true;
  3726. }
  3727. return false;
  3728. }
  3729. void PhonemeEditor::OnImport()
  3730. {
  3731. char filename[ 512 ];
  3732. if ( !FacePoser_ShowOpenFileNameDialog( filename, sizeof( filename ), "sound", "*" WORD_DATA_EXTENSION ) )
  3733. {
  3734. return;
  3735. }
  3736. ImportValveDataChunk( filename );
  3737. }
  3738. void PhonemeEditor::OnExport()
  3739. {
  3740. if ( !m_pWaveFile )
  3741. return;
  3742. char filename[ 512 ];
  3743. if ( !FacePoser_ShowSaveFileNameDialog( filename, sizeof( filename ), "sound", "*" WORD_DATA_EXTENSION ) )
  3744. {
  3745. return;
  3746. }
  3747. Q_SetExtension( filename, WORD_DATA_EXTENSION, sizeof( filename ) );
  3748. ExportValveDataChunk( filename );
  3749. }
  3750. //-----------------------------------------------------------------------------
  3751. // Purpose:
  3752. // Input : store -
  3753. //-----------------------------------------------------------------------------
  3754. void PhonemeEditor::StoreValveDataChunk( IterateOutputRIFF& store )
  3755. {
  3756. // Buffer and dump data
  3757. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  3758. m_Tags.SaveToBuffer( buf );
  3759. // Copy into store
  3760. store.ChunkWriteData( buf.Base(), buf.TellPut() );
  3761. }
  3762. //-----------------------------------------------------------------------------
  3763. // Purpose:
  3764. // Input : *tempfile -
  3765. //-----------------------------------------------------------------------------
  3766. void PhonemeEditor::ExportValveDataChunk( char const *tempfile )
  3767. {
  3768. if ( m_Tags.m_Words.Count() <= 0 )
  3769. {
  3770. Con_ErrorPrintf( "PhonemeEditor::ExportValveDataChunk: Sentence has no word data\n" );
  3771. return;
  3772. }
  3773. FileHandle_t fh = filesystem->Open( tempfile, "wb" );
  3774. if ( !fh )
  3775. {
  3776. Con_ErrorPrintf( "PhonemeEditor::ExportValveDataChunk: Unable to write to %s (read-only?)\n", tempfile );
  3777. return;
  3778. }
  3779. else
  3780. {
  3781. // Buffer and dump data
  3782. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  3783. m_Tags.SaveToBuffer( buf );
  3784. filesystem->Write( buf.Base(), buf.TellPut(), fh );
  3785. filesystem->Close(fh);
  3786. Con_Printf( "Exported %i words to %s\n", m_Tags.m_Words.Count(), tempfile );
  3787. }
  3788. }
  3789. //-----------------------------------------------------------------------------
  3790. // Purpose:
  3791. // Input : *tempfile -
  3792. //-----------------------------------------------------------------------------
  3793. void PhonemeEditor::ImportValveDataChunk( char const *tempfile )
  3794. {
  3795. FileHandle_t fh = filesystem->Open( tempfile, "rb" );
  3796. if ( !fh )
  3797. {
  3798. Con_ErrorPrintf( "PhonemeEditor::ImportValveDataChunk: Unable to read from %s\n", tempfile );
  3799. return;
  3800. }
  3801. int len = filesystem->Size( fh );
  3802. if ( len <= 4 )
  3803. {
  3804. Con_ErrorPrintf( "PhonemeEditor::ImportValveDataChunk: File %s has length 0\n", tempfile );
  3805. return;
  3806. }
  3807. ClearExtracted();
  3808. unsigned char *buf = new unsigned char[ len + 1 ];
  3809. filesystem->Read( buf, len, fh );
  3810. filesystem->Close( fh );
  3811. m_TagsExt.InitFromDataChunk( (void *)( buf ), len );
  3812. delete[] buf;
  3813. Con_Printf( "Imported %i words from %s\n", m_TagsExt.m_Words.Count(), tempfile );
  3814. redraw();
  3815. }
  3816. //-----------------------------------------------------------------------------
  3817. // Purpose: Copy file over, but update phoneme lump with new data
  3818. //-----------------------------------------------------------------------------
  3819. void PhonemeEditor::SaveLinguisticData( void )
  3820. {
  3821. if ( !m_pWaveFile )
  3822. return;
  3823. InFileRIFF riff( m_WorkFile.m_szWaveFile, io_in );
  3824. Assert( riff.RIFFName() == RIFF_WAVE );
  3825. // set up the iterator for the whole file (root RIFF is a chunk)
  3826. IterateRIFF walk( riff, riff.RIFFSize() );
  3827. char fullout[ 512 ];
  3828. Q_snprintf( fullout, sizeof( fullout ), "%s%s", m_WorkFile.m_szBasePath, m_WorkFile.m_szWorkingFile );
  3829. OutFileRIFF riffout( fullout, io_out );
  3830. IterateOutputRIFF store( riffout );
  3831. bool formatset = false;
  3832. WAVEFORMATEX format;
  3833. bool wordtrackwritten = false;
  3834. // Walk input chunks and copy to output
  3835. while ( walk.ChunkAvailable() )
  3836. {
  3837. unsigned int originalPos = store.ChunkGetPosition();
  3838. store.ChunkStart( walk.ChunkName() );
  3839. bool skipchunk = false;
  3840. switch ( walk.ChunkName() )
  3841. {
  3842. case WAVE_VALVEDATA:
  3843. // Overwrite data
  3844. StoreValveDataChunk( store );
  3845. wordtrackwritten = true;
  3846. break;
  3847. case WAVE_FMT:
  3848. {
  3849. formatset = true;
  3850. char *buffer = new char[ walk.ChunkSize() ];
  3851. Assert( buffer );
  3852. walk.ChunkRead( buffer );
  3853. format = *(WAVEFORMATEX *)buffer;
  3854. store.ChunkWriteData( buffer, walk.ChunkSize() );
  3855. delete[] buffer;
  3856. }
  3857. break;
  3858. case WAVE_DATA:
  3859. {
  3860. Assert( formatset );
  3861. char *buffer = new char[ walk.ChunkSize() ];
  3862. Assert( buffer );
  3863. walk.ChunkRead( buffer );
  3864. // Resample it
  3865. ResampleChunk( store, (void *)&format, walk.ChunkName(), buffer, walk.ChunkSize() );
  3866. delete[] buffer;
  3867. }
  3868. break;
  3869. default:
  3870. store.CopyChunkData( walk );
  3871. break;
  3872. }
  3873. store.ChunkFinish();
  3874. if ( skipchunk )
  3875. {
  3876. store.ChunkSetPosition( originalPos );
  3877. }
  3878. walk.ChunkNext();
  3879. }
  3880. if ( !wordtrackwritten )
  3881. {
  3882. store.ChunkStart( WAVE_VALVEDATA );
  3883. StoreValveDataChunk( store );
  3884. store.ChunkFinish();
  3885. }
  3886. }
  3887. //-----------------------------------------------------------------------------
  3888. // Purpose: Copy phoneme data in from wave file we sent for resprocessing
  3889. //-----------------------------------------------------------------------------
  3890. void PhonemeEditor::RetrieveLinguisticData( void )
  3891. {
  3892. if ( !m_pWaveFile )
  3893. return;
  3894. m_Tags.Reset();
  3895. ReadLinguisticTags();
  3896. redraw();
  3897. }
  3898. bool PhonemeEditor::StopPlayback( void )
  3899. {
  3900. bool bret = false;
  3901. if ( m_pWaveFile )
  3902. {
  3903. SetScrubTargetTime( m_flScrub );
  3904. if ( sound->IsSoundPlaying( m_pMixer ) )
  3905. {
  3906. sound->StopAll();
  3907. bret = true;
  3908. }
  3909. }
  3910. sound->Flush();
  3911. return bret;
  3912. }
  3913. CPhonemeTag *PhonemeEditor::GetPhonemeTagUnderMouse( int mx, int my )
  3914. {
  3915. if ( GetMode() != MODE_PHONEMES )
  3916. return NULL;
  3917. if ( !m_pWaveFile )
  3918. return NULL;
  3919. // FIXME: Don't read from file, read from arrays after LISET finishes
  3920. // Deterime if phoneme boundary is under the cursor
  3921. //
  3922. RECT rc;
  3923. GetWorkspaceRect( rc );
  3924. if ( !IsMouseOverPhonemeRow( my ) )
  3925. return NULL;
  3926. if ( GetPixelsPerSecond() <= 0 )
  3927. return NULL;
  3928. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  3929. float endtime = w2() / GetPixelsPerSecond() + starttime;
  3930. if ( endtime - starttime <= 0.0f )
  3931. return NULL;
  3932. for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
  3933. {
  3934. CWordTag *word = m_Tags.m_Words[ i ];
  3935. Assert( word );
  3936. if ( !word )
  3937. continue;
  3938. for ( int k = 0; k < word->m_Phonemes.Size(); k++ )
  3939. {
  3940. CPhonemeTag *pPhoneme = word->m_Phonemes[ k ];
  3941. Assert( pPhoneme );
  3942. if ( !pPhoneme )
  3943. continue;
  3944. float t1 = pPhoneme->GetStartTime();
  3945. float t2 = pPhoneme->GetEndTime();
  3946. float frac1 = ( t1 - starttime ) / ( endtime - starttime );
  3947. float frac2 = ( t2 - starttime ) / ( endtime - starttime );
  3948. frac1 = min( 1.0f, frac1 );
  3949. frac1 = max( 0.0f, frac1 );
  3950. frac2 = min( 1.0f, frac2 );
  3951. frac2 = max( 0.0f, frac2 );
  3952. if ( frac1 == frac2 )
  3953. continue;
  3954. int x1 = ( int )( frac1 * w2() );
  3955. int x2 = ( int )( frac2 * w2() );
  3956. if ( mx >= x1 && mx <= x2 )
  3957. {
  3958. return pPhoneme;
  3959. }
  3960. }
  3961. }
  3962. return NULL;
  3963. }
  3964. CWordTag *PhonemeEditor::GetWordTagUnderMouse( int mx, int my )
  3965. {
  3966. if ( GetMode() != MODE_PHONEMES )
  3967. return NULL;
  3968. // Deterime if phoneme boundary is under the cursor
  3969. //
  3970. if ( !m_pWaveFile )
  3971. return NULL;
  3972. RECT rc;
  3973. GetWorkspaceRect( rc );
  3974. if ( !IsMouseOverWordRow( my ) )
  3975. return NULL;
  3976. if ( GetPixelsPerSecond() <= 0 )
  3977. return NULL;
  3978. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  3979. float endtime = w2() / GetPixelsPerSecond() + starttime;
  3980. if ( endtime - starttime <= 0.0f )
  3981. return NULL;
  3982. for ( int k = 0; k < m_Tags.m_Words.Size(); k++ )
  3983. {
  3984. CWordTag *word = m_Tags.m_Words[ k ];
  3985. Assert( word );
  3986. if ( !word )
  3987. continue;
  3988. float t1 = word->m_flStartTime;
  3989. float t2 = word->m_flEndTime;
  3990. float frac1 = ( t1 - starttime ) / ( endtime - starttime );
  3991. float frac2 = ( t2 - starttime ) / ( endtime - starttime );
  3992. frac1 = min( 1.0f, frac1 );
  3993. frac1 = max( 0.0f, frac1 );
  3994. frac2 = min( 1.0f, frac2 );
  3995. frac2 = max( 0.0f, frac2 );
  3996. if ( frac1 == frac2 )
  3997. continue;
  3998. int x1 = ( int )( frac1 * w2() );
  3999. int x2 = ( int )( frac2 * w2() );
  4000. if ( mx >= x1 && mx <= x2 )
  4001. {
  4002. return word;
  4003. }
  4004. }
  4005. return NULL;
  4006. }
  4007. void PhonemeEditor::DeselectWords( void )
  4008. {
  4009. if ( GetMode() != MODE_PHONEMES )
  4010. return;
  4011. for ( int i = 0 ; i < m_Tags.m_Words.Size(); i++ )
  4012. {
  4013. CWordTag *w = m_Tags.m_Words[ i ];
  4014. Assert( w );
  4015. if ( !w )
  4016. continue;
  4017. w->m_bSelected = false;
  4018. }
  4019. }
  4020. void PhonemeEditor::DeselectPhonemes( void )
  4021. {
  4022. if ( GetMode() != MODE_PHONEMES )
  4023. return;
  4024. for ( int w = 0 ; w < m_Tags.m_Words.Size(); w++ )
  4025. {
  4026. CWordTag *word = m_Tags.m_Words[ w ];
  4027. Assert( word );
  4028. if ( !word )
  4029. continue;
  4030. for ( int i = 0 ; i < word->m_Phonemes.Size(); i++ )
  4031. {
  4032. CPhonemeTag *pt = word->m_Phonemes[ i ];
  4033. Assert( pt );
  4034. if ( !pt )
  4035. continue;
  4036. pt->m_bSelected = false;
  4037. }
  4038. }
  4039. }
  4040. void PhonemeEditor::SnapWords( void )
  4041. {
  4042. if ( GetMode() != MODE_PHONEMES )
  4043. return;
  4044. if ( m_Tags.m_Words.Size() < 2 )
  4045. {
  4046. Con_Printf( "Can't snap, need at least two contiguous selected words\n" );
  4047. return;
  4048. }
  4049. SetDirty( true );
  4050. PushUndo();
  4051. for ( int i = 0; i < m_Tags.m_Words.Size() - 1; i++ )
  4052. {
  4053. CWordTag *current = m_Tags.m_Words[ i ];
  4054. CWordTag *next = m_Tags.m_Words[ i + 1 ];
  4055. Assert( current && next );
  4056. if ( !current->m_bSelected || !next->m_bSelected )
  4057. continue;
  4058. // Move next word to end of current
  4059. next->m_flStartTime = current->m_flEndTime;
  4060. }
  4061. PushRedo();
  4062. redraw();
  4063. }
  4064. void PhonemeEditor::SeparateWords( void )
  4065. {
  4066. if ( GetMode() != MODE_PHONEMES )
  4067. return;
  4068. if ( GetPixelsPerSecond() <= 0.0f )
  4069. return;
  4070. if ( m_Tags.m_Words.Size() < 2 )
  4071. {
  4072. Con_Printf( "Can't separate, need at least two contiguous selected words\n" );
  4073. return;
  4074. }
  4075. // Three pixels
  4076. double time_epsilon = ( 1.0f / GetPixelsPerSecond() ) * 6;
  4077. SetDirty( true );
  4078. PushUndo();
  4079. for ( int i = 0; i < m_Tags.m_Words.Size() - 1; i++ )
  4080. {
  4081. CWordTag *current = m_Tags.m_Words[ i ];
  4082. CWordTag *next = m_Tags.m_Words[ i + 1 ];
  4083. Assert( current && next );
  4084. if ( !current->m_bSelected || !next->m_bSelected )
  4085. continue;
  4086. // Close enough?
  4087. if ( fabs( current->m_flEndTime - next->m_flStartTime ) > time_epsilon )
  4088. {
  4089. Con_Printf( "Can't split %s and %s, already split apart\n",
  4090. current->GetWord(), next->GetWord() );
  4091. continue;
  4092. }
  4093. // Offset next word start time a bit
  4094. next->m_flStartTime += time_epsilon;
  4095. break;
  4096. }
  4097. PushRedo();
  4098. redraw();
  4099. }
  4100. void PhonemeEditor::CreateEvenWordDistribution( const char *wordlist )
  4101. {
  4102. if ( GetMode() != MODE_PHONEMES )
  4103. return;
  4104. if( !m_pWaveFile )
  4105. return;
  4106. Assert( wordlist );
  4107. if ( !wordlist )
  4108. return;
  4109. m_Tags.CreateEventWordDistribution( wordlist, m_pWaveFile->GetRunningLength() );
  4110. redraw();
  4111. }
  4112. void PhonemeEditor::EditWordList( void )
  4113. {
  4114. if ( GetMode() != MODE_PHONEMES )
  4115. return;
  4116. if ( !m_pWaveFile )
  4117. return;
  4118. // Build word string
  4119. char wordstring[ 1024 ];
  4120. V_strcpy_safe( wordstring, m_Tags.GetText() );
  4121. CInputParams params;
  4122. memset( &params, 0, sizeof( params ) );
  4123. strcpy( params.m_szDialogTitle, "Word List" );
  4124. strcpy( params.m_szPrompt, "Sentence:" );
  4125. strcpy( params.m_szInputText, wordstring );
  4126. if ( !InputProperties( &params ) )
  4127. return;
  4128. if ( strlen( params.m_szInputText ) <= 0 )
  4129. {
  4130. // Could be foreign language...
  4131. Warning( "Edit word list: No words entered!\n" );
  4132. }
  4133. SetDirty( true );
  4134. PushUndo();
  4135. // Clear any current LISET results
  4136. ClearExtracted();
  4137. // Force text
  4138. m_Tags.SetText( params.m_szInputText );
  4139. if ( m_Tags.m_Words.Size() == 0 )
  4140. {
  4141. // First text we've seen, just distribute words evenly
  4142. CreateEvenWordDistribution( params.m_szInputText );
  4143. // Redo liset
  4144. RedoPhonemeExtraction();
  4145. }
  4146. PushRedo();
  4147. SetFocus( (HWND)getHandle() );
  4148. redraw();
  4149. }
  4150. //-----------------------------------------------------------------------------
  4151. // Purpose: Overwrite original wave with changes
  4152. //-----------------------------------------------------------------------------
  4153. void PhonemeEditor::CommitChanges( void )
  4154. {
  4155. SaveLinguisticData();
  4156. // Make it writable - if possible
  4157. MakeFileWriteable( m_WorkFile.m_szWaveFile );
  4158. //Open a message box to warn the user if the file was unable to be made non-read only
  4159. if ( !IsFileWriteable( m_WorkFile.m_szWaveFile ) )
  4160. {
  4161. mxMessageBox( NULL, va( "Unable to save file '%s'. File is read-only or in use.",
  4162. m_WorkFile.m_szWaveFile ), g_appTitle, MX_MB_OK );
  4163. }
  4164. else
  4165. {
  4166. // Copy over and overwrite file
  4167. FPCopyFile( m_WorkFile.m_szWorkingFile, m_WorkFile.m_szWaveFile, true );
  4168. Msg( "Changes saved to '%s'\n", m_WorkFile.m_szWaveFile );
  4169. SetDirty( false, false );
  4170. }
  4171. }
  4172. //-----------------------------------------------------------------------------
  4173. // Purpose:
  4174. //-----------------------------------------------------------------------------
  4175. void PhonemeEditor::LoadWaveFile( void )
  4176. {
  4177. char filename[ 512 ];
  4178. if ( !FacePoser_ShowOpenFileNameDialog( filename, sizeof( filename ), "sound", "*.wav" ) )
  4179. {
  4180. return;
  4181. }
  4182. StopPlayback();
  4183. // Strip out the game directory
  4184. SetCurrentWaveFile( filename );
  4185. }
  4186. void PhonemeEditor::SnapPhonemes( void )
  4187. {
  4188. if ( GetMode() != MODE_PHONEMES )
  4189. return;
  4190. SetDirty( true );
  4191. PushUndo();
  4192. CPhonemeTag *prev = NULL;
  4193. for ( int w = 0; w < m_Tags.m_Words.Size(); w++ )
  4194. {
  4195. CWordTag *word = m_Tags.m_Words[ w ];
  4196. Assert( word );
  4197. if ( !word )
  4198. continue;
  4199. for ( int i = 0; i < word->m_Phonemes.Size(); i++ )
  4200. {
  4201. CPhonemeTag *current = word->m_Phonemes[ i ];
  4202. Assert( current );
  4203. if ( current->m_bSelected )
  4204. {
  4205. if (prev)
  4206. {
  4207. // More start of next to end of previous
  4208. prev->SetEndTime( current->GetStartTime() );
  4209. }
  4210. prev = current;
  4211. }
  4212. else
  4213. {
  4214. prev = NULL;
  4215. }
  4216. }
  4217. }
  4218. PushRedo();
  4219. redraw();
  4220. }
  4221. void PhonemeEditor::SeparatePhonemes( void )
  4222. {
  4223. if ( GetMode() != MODE_PHONEMES )
  4224. return;
  4225. SetDirty( true );
  4226. PushUndo();
  4227. // Three pixels
  4228. double time_epsilon = ( 1.0f / GetPixelsPerSecond() ) * 6;
  4229. CPhonemeTag *prev = NULL;
  4230. for ( int w = 0; w < m_Tags.m_Words.Size(); w++ )
  4231. {
  4232. CWordTag *word = m_Tags.m_Words[ w ];
  4233. Assert( word );
  4234. if ( !word )
  4235. continue;
  4236. for ( int i = 0; i < word->m_Phonemes.Size(); i++ )
  4237. {
  4238. CPhonemeTag *current = word->m_Phonemes[ i ];
  4239. Assert( current );
  4240. if ( current->m_bSelected )
  4241. {
  4242. if ( prev )
  4243. {
  4244. // Close enough?
  4245. if ( fabs( prev->GetEndTime() - current->GetStartTime() ) > time_epsilon )
  4246. {
  4247. Con_Printf( "Can't split already split apart\n" );
  4248. continue;
  4249. }
  4250. current->AddStartTime( time_epsilon );
  4251. }
  4252. prev = current;
  4253. }
  4254. else
  4255. {
  4256. prev = NULL;
  4257. }
  4258. }
  4259. }
  4260. PushRedo();
  4261. redraw();
  4262. }
  4263. bool PhonemeEditor::IsMouseOverWordRow( int my )
  4264. {
  4265. if ( GetMode() != MODE_PHONEMES )
  4266. return false;
  4267. RECT rc;
  4268. GetWordTrayTopBottom( rc );
  4269. if ( my < rc.top )
  4270. return false;
  4271. if ( my > rc.bottom )
  4272. return false;
  4273. return true;
  4274. }
  4275. bool PhonemeEditor::IsMouseOverPhonemeRow( int my )
  4276. {
  4277. if ( GetMode() != MODE_PHONEMES )
  4278. return false;
  4279. RECT rc;
  4280. GetPhonemeTrayTopBottom( rc );
  4281. if ( my < rc.top )
  4282. return false;
  4283. if ( my > rc.bottom )
  4284. return false;
  4285. return true;
  4286. }
  4287. void PhonemeEditor::GetPhonemeTrayTopBottom( RECT& rc )
  4288. {
  4289. RECT wkrc;
  4290. GetWorkspaceRect( wkrc );
  4291. rc.top = wkrc.bottom - 2 * m_nTickHeight;
  4292. rc.bottom = wkrc.bottom - m_nTickHeight;
  4293. }
  4294. void PhonemeEditor::GetWordTrayTopBottom( RECT& rc )
  4295. {
  4296. RECT wkrc;
  4297. GetWorkspaceRect( wkrc );
  4298. rc.top = wkrc.top;
  4299. rc.bottom = wkrc.top + m_nTickHeight;
  4300. }
  4301. int PhonemeEditor::GetMouseForTime( float time )
  4302. {
  4303. RECT rc;
  4304. GetWorkspaceRect( rc );
  4305. if ( GetPixelsPerSecond() < 0.0f )
  4306. return rc.left;
  4307. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  4308. float endtime = w2() / GetPixelsPerSecond() + starttime;
  4309. if ( endtime - starttime <= 0.0f )
  4310. return rc.left;
  4311. float frac;
  4312. frac = ( time - starttime ) / ( endtime - starttime );
  4313. return rc.left + ( int )( rc.right * frac );
  4314. }
  4315. void PhonemeEditor::GetWordRect( const CWordTag *tag, RECT& rc )
  4316. {
  4317. Assert( tag );
  4318. GetWordTrayTopBottom( rc );
  4319. rc.left = GetMouseForTime( tag->m_flStartTime );
  4320. rc.right = GetMouseForTime( tag->m_flEndTime );
  4321. }
  4322. void PhonemeEditor::GetPhonemeRect( const CPhonemeTag *tag, RECT& rc )
  4323. {
  4324. Assert( tag );
  4325. GetPhonemeTrayTopBottom( rc );
  4326. rc.left = GetMouseForTime( tag->GetStartTime() );
  4327. rc.right = GetMouseForTime( tag->GetEndTime() );
  4328. }
  4329. //-----------------------------------------------------------------------------
  4330. // Purpose:
  4331. //-----------------------------------------------------------------------------
  4332. void PhonemeEditor::CommitExtracted( void )
  4333. {
  4334. if ( GetMode() != MODE_PHONEMES )
  4335. return;
  4336. m_nLastExtractionResult = SR_RESULT_NORESULT;
  4337. if ( !m_TagsExt.m_Words.Size() )
  4338. return;
  4339. SetDirty( true );
  4340. PushUndo();
  4341. m_Tags.Reset();
  4342. m_Tags = m_TagsExt;
  4343. PushRedo();
  4344. ClearExtracted();
  4345. redraw();
  4346. }
  4347. //-----------------------------------------------------------------------------
  4348. // Purpose:
  4349. //-----------------------------------------------------------------------------
  4350. void PhonemeEditor::ClearExtracted( void )
  4351. {
  4352. if ( GetMode() != MODE_PHONEMES )
  4353. return;
  4354. m_nLastExtractionResult = SR_RESULT_NORESULT;
  4355. m_TagsExt.Reset();
  4356. redraw();
  4357. }
  4358. //-----------------------------------------------------------------------------
  4359. // Purpose:
  4360. // Input : resultCode -
  4361. // Output : const char
  4362. //-----------------------------------------------------------------------------
  4363. const char *PhonemeEditor::GetExtractionResultString( int resultCode )
  4364. {
  4365. switch ( resultCode )
  4366. {
  4367. case SR_RESULT_NORESULT:
  4368. return "no extraction info.";
  4369. case SR_RESULT_ERROR:
  4370. return "an error occurred during extraction.";
  4371. case SR_RESULT_SUCCESS:
  4372. return "successful.";
  4373. case SR_RESULT_FAILED:
  4374. return "results retrieved, but full recognition failed.";
  4375. default:
  4376. break;
  4377. }
  4378. return "unknown result code.";
  4379. }
  4380. //-----------------------------------------------------------------------------
  4381. // Purpose:
  4382. // Input : mx -
  4383. // Output : CEventRelativeTag
  4384. //-----------------------------------------------------------------------------
  4385. CEventRelativeTag *PhonemeEditor::GetTagUnderMouse( int mx )
  4386. {
  4387. if ( GetMode() != MODE_PHONEMES )
  4388. return NULL;
  4389. // Figure out tag positions
  4390. if ( !m_pEvent || !m_pWaveFile )
  4391. return NULL;
  4392. RECT rc;
  4393. GetWorkspaceRect( rc );
  4394. RECT rcTags = rc;
  4395. if ( GetPixelsPerSecond() <= 0.0f )
  4396. return NULL;
  4397. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  4398. float endtime = w2() / GetPixelsPerSecond() + starttime;
  4399. if ( endtime - starttime < 0 )
  4400. return NULL;
  4401. for ( int i = 0; i < m_pEvent->GetNumRelativeTags(); i++ )
  4402. {
  4403. CEventRelativeTag *tag = m_pEvent->GetRelativeTag( i );
  4404. if ( !tag )
  4405. continue;
  4406. //
  4407. float tagtime = tag->GetPercentage() * m_pWaveFile->GetRunningLength();
  4408. if ( tagtime < starttime || tagtime > endtime )
  4409. continue;
  4410. float frac = ( tagtime - starttime ) / ( endtime - starttime );
  4411. int left = rcTags.left + (int)( frac * ( float )( rcTags.right - rcTags.left ) + 0.5f );
  4412. if ( abs( mx - left ) < 10 )
  4413. return tag;
  4414. }
  4415. return NULL;
  4416. }
  4417. //-----------------------------------------------------------------------------
  4418. // Purpose:
  4419. // Input : mx -
  4420. // my -
  4421. // Output : Returns true on success, false on failure.
  4422. //-----------------------------------------------------------------------------
  4423. bool PhonemeEditor::IsMouseOverTag( int mx, int my )
  4424. {
  4425. if ( GetMode() != MODE_PHONEMES )
  4426. return false;
  4427. if ( !IsMouseOverTagRow( my ) )
  4428. return false;
  4429. CEventRelativeTag *tag = GetTagUnderMouse( mx );
  4430. if ( !tag )
  4431. return false;
  4432. return true;
  4433. }
  4434. //-----------------------------------------------------------------------------
  4435. // Purpose:
  4436. // Input : startx -
  4437. // endx -
  4438. //-----------------------------------------------------------------------------
  4439. void PhonemeEditor::FinishEventTagDrag( int startx, int endx )
  4440. {
  4441. if ( !m_pWaveFile )
  4442. return;
  4443. if ( !m_pWaveFile->GetRunningLength() )
  4444. return;
  4445. // Find starting tag
  4446. CEventRelativeTag *tag = GetTagUnderMouse( startx );
  4447. if ( !tag )
  4448. return;
  4449. if ( GetPixelsPerSecond() <= 0 )
  4450. return;
  4451. // Convert mouse position to time
  4452. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  4453. float clicktime = (float)endx / GetPixelsPerSecond() + starttime;
  4454. float percent = clicktime / m_pWaveFile->GetRunningLength();
  4455. percent = clamp( percent, 0.0f, 1.0f );
  4456. tag->SetPercentage( percent );
  4457. redraw();
  4458. if ( g_pChoreoView )
  4459. {
  4460. g_pChoreoView->InvalidateLayout();
  4461. }
  4462. }
  4463. //-----------------------------------------------------------------------------
  4464. // Purpose:
  4465. // Input : my -
  4466. // Output : Returns true on success, false on failure.
  4467. //-----------------------------------------------------------------------------
  4468. bool PhonemeEditor::IsMouseOverTagRow( int my )
  4469. {
  4470. if ( GetMode() != MODE_PHONEMES )
  4471. return false;
  4472. if ( my < TAG_TOP || my > TAG_BOTTOM )
  4473. return false;
  4474. return true;
  4475. }
  4476. //-----------------------------------------------------------------------------
  4477. // Purpose:
  4478. // Input : mx -
  4479. // my -
  4480. //-----------------------------------------------------------------------------
  4481. void PhonemeEditor::ShowTagMenu( int mx, int my )
  4482. {
  4483. if ( GetMode() != MODE_PHONEMES )
  4484. return;
  4485. // Figure out tag positions
  4486. if ( !m_pEvent || !m_pWaveFile )
  4487. return;
  4488. if ( !IsMouseOverTagRow( my ) )
  4489. return;
  4490. CEventRelativeTag *tag = GetTagUnderMouse( mx );
  4491. mxPopupMenu *pop = new mxPopupMenu();
  4492. if ( tag )
  4493. {
  4494. pop->add( va( "Delete tag '%s'", tag->GetName() ), IDC_DELETETAG );
  4495. }
  4496. else
  4497. {
  4498. pop->add( va( "Add tag..." ), IDC_ADDTAG );
  4499. }
  4500. m_nClickX = mx;
  4501. pop->popup( this, mx, my );
  4502. }
  4503. //-----------------------------------------------------------------------------
  4504. // Purpose:
  4505. //-----------------------------------------------------------------------------
  4506. void PhonemeEditor::DeleteTag( void )
  4507. {
  4508. if ( GetMode() != MODE_PHONEMES )
  4509. return;
  4510. // Figure out tag positions
  4511. if ( !m_pEvent )
  4512. return;
  4513. CEventRelativeTag *tag = GetTagUnderMouse( m_nClickX );
  4514. if ( !tag )
  4515. return;
  4516. // Remove it
  4517. m_pEvent->RemoveRelativeTag( tag->GetName() );
  4518. g_pChoreoView->InvalidateLayout();
  4519. redraw();
  4520. }
  4521. //-----------------------------------------------------------------------------
  4522. // Purpose:
  4523. //-----------------------------------------------------------------------------
  4524. void PhonemeEditor::AddTag( void )
  4525. {
  4526. if ( GetMode() != MODE_PHONEMES )
  4527. return;
  4528. // Figure out tag positions
  4529. if ( !m_pEvent || !m_pWaveFile )
  4530. return;
  4531. CInputParams params;
  4532. memset( &params, 0, sizeof( params ) );
  4533. strcpy( params.m_szDialogTitle, "Event Tag Name" );
  4534. strcpy( params.m_szPrompt, "Name:" );
  4535. strcpy( params.m_szInputText, "" );
  4536. if ( !InputProperties( &params ) )
  4537. return;
  4538. if ( strlen( params.m_szInputText ) <= 0 )
  4539. {
  4540. Con_ErrorPrintf( "Event Tag Name: No name entered!\n" );
  4541. return;
  4542. }
  4543. // Convert mouse position to time
  4544. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  4545. float clicktime = (float)m_nClickX / GetPixelsPerSecond() + starttime;
  4546. float percent = clicktime / m_pWaveFile->GetRunningLength();
  4547. percent = min( 1.0f, percent );
  4548. percent = max( 0.0f, percent );
  4549. m_pEvent->AddRelativeTag( params.m_szInputText, percent );
  4550. g_pChoreoView->InvalidateLayout();
  4551. SetFocus( (HWND)getHandle() );
  4552. redraw();
  4553. }
  4554. //-----------------------------------------------------------------------------
  4555. // Purpose:
  4556. //-----------------------------------------------------------------------------
  4557. void PhonemeEditor::ClearEvent( void )
  4558. {
  4559. m_pEvent = NULL;
  4560. redraw();
  4561. }
  4562. void PhonemeEditor::TraverseWords( PEWORDITERFUNC pfn, float fparam )
  4563. {
  4564. for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
  4565. {
  4566. CWordTag *word = m_Tags.m_Words[ i ];
  4567. if ( !word )
  4568. continue;
  4569. (this->*pfn)( word, fparam );
  4570. }
  4571. }
  4572. void PhonemeEditor::TraversePhonemes( PEPHONEMEITERFUNC pfn, float fparam )
  4573. {
  4574. for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
  4575. {
  4576. CWordTag *word = m_Tags.m_Words[ i ];
  4577. if ( !word )
  4578. continue;
  4579. for ( int j = 0; j < word->m_Phonemes.Size(); j++ )
  4580. {
  4581. CPhonemeTag *phoneme = word->m_Phonemes[ j ];
  4582. if ( !phoneme )
  4583. continue;
  4584. (this->*pfn)( phoneme, word, fparam );
  4585. }
  4586. }
  4587. }
  4588. //-----------------------------------------------------------------------------
  4589. // Purpose:
  4590. // Input : amount -
  4591. //-----------------------------------------------------------------------------
  4592. void PhonemeEditor::ITER_MoveSelectedWords( CWordTag *word, float amount )
  4593. {
  4594. if ( !word->m_bSelected )
  4595. return;
  4596. word->m_flStartTime += amount;
  4597. word->m_flEndTime += amount;
  4598. }
  4599. void PhonemeEditor::ITER_MoveSelectedPhonemes( CPhonemeTag *phoneme, CWordTag *word, float amount )
  4600. {
  4601. if ( !phoneme->m_bSelected )
  4602. return;
  4603. phoneme->AddStartTime( amount );
  4604. phoneme->AddEndTime( amount );
  4605. }
  4606. void PhonemeEditor::ITER_ExtendSelectedPhonemeEndTimes( CPhonemeTag *phoneme, CWordTag *word, float amount )
  4607. {
  4608. if ( !phoneme->m_bSelected )
  4609. return;
  4610. if ( phoneme->GetEndTime() + amount <= phoneme->GetStartTime() )
  4611. return;
  4612. phoneme->AddEndTime( amount );
  4613. // Fixme, check for extending into next phoneme
  4614. }
  4615. void PhonemeEditor::ITER_ExtendSelectedWordEndTimes( CWordTag *word, float amount )
  4616. {
  4617. if ( !word->m_bSelected )
  4618. return;
  4619. if ( word->m_flEndTime + amount <= word->m_flStartTime )
  4620. return;
  4621. word->m_flEndTime += amount;
  4622. // Fixme, check for extending into next word
  4623. }
  4624. void PhonemeEditor::ITER_AddFocusRectSelectedWords( CWordTag *word, float amount )
  4625. {
  4626. if ( !word->m_bSelected )
  4627. return;
  4628. RECT wordRect;
  4629. GetWordRect( word, wordRect );
  4630. AddFocusRect( wordRect );
  4631. }
  4632. void PhonemeEditor::ITER_AddFocusRectSelectedPhonemes( CPhonemeTag *phoneme, CWordTag *word, float amount )
  4633. {
  4634. if ( !phoneme->m_bSelected )
  4635. return;
  4636. RECT phonemeRect;
  4637. GetPhonemeRect( phoneme, phonemeRect );
  4638. AddFocusRect( phonemeRect );
  4639. }
  4640. void PhonemeEditor::AddFocusRect( RECT& rc )
  4641. {
  4642. RECT rcFocus = rc;
  4643. POINT offset;
  4644. offset.x = 0;
  4645. offset.y = 0;
  4646. ClientToScreen( (HWND)getHandle(), &offset );
  4647. OffsetRect( &rcFocus, offset.x, offset.y );
  4648. // Convert to screen space?
  4649. CFocusRect fr;
  4650. fr.m_rcFocus = rcFocus;
  4651. fr.m_rcOrig = rcFocus;
  4652. m_FocusRects.AddToTail( fr );
  4653. }
  4654. void PhonemeEditor::CountSelected( void )
  4655. {
  4656. m_nSelectedPhonemeCount = 0;
  4657. m_nSelectedWordCount = 0;
  4658. TraverseWords( &PhonemeEditor::ITER_CountSelectedWords, 0.0f );
  4659. TraversePhonemes( &PhonemeEditor::ITER_CountSelectedPhonemes, 0.0f );
  4660. }
  4661. void PhonemeEditor::ITER_CountSelectedWords( CWordTag *word, float amount )
  4662. {
  4663. if ( !word->m_bSelected )
  4664. return;
  4665. m_nSelectedWordCount++;
  4666. }
  4667. void PhonemeEditor::ITER_CountSelectedPhonemes( CPhonemeTag *phoneme, CWordTag *word, float amount )
  4668. {
  4669. if ( !phoneme->m_bSelected )
  4670. return;
  4671. m_nSelectedPhonemeCount++;
  4672. }
  4673. // Undo/Redo
  4674. void PhonemeEditor::Undo( void )
  4675. {
  4676. if ( m_UndoStack.Size() > 0 && m_nUndoLevel > 0 )
  4677. {
  4678. m_nUndoLevel--;
  4679. PEUndo *u = m_UndoStack[ m_nUndoLevel ];
  4680. Assert( u->undo );
  4681. m_Tags = *(u->undo);
  4682. SetClickedPhoneme( -1, -1 );
  4683. }
  4684. redraw();
  4685. }
  4686. void PhonemeEditor::Redo( void )
  4687. {
  4688. if ( m_UndoStack.Size() > 0 && m_nUndoLevel <= m_UndoStack.Size() - 1 )
  4689. {
  4690. PEUndo *u = m_UndoStack[ m_nUndoLevel ];
  4691. Assert( u->redo );
  4692. m_Tags = *(u->redo);
  4693. m_nUndoLevel++;
  4694. SetClickedPhoneme( -1, -1 );
  4695. }
  4696. redraw();
  4697. }
  4698. void PhonemeEditor::PushUndo( void )
  4699. {
  4700. Assert( !m_bRedoPending );
  4701. m_bRedoPending = true;
  4702. WipeRedo();
  4703. // Copy current data
  4704. CSentence *u = new CSentence();
  4705. *u = m_Tags;
  4706. PEUndo *undo = new PEUndo;
  4707. undo->undo = u;
  4708. undo->redo = NULL;
  4709. m_UndoStack.AddToTail( undo );
  4710. m_nUndoLevel++;
  4711. }
  4712. void PhonemeEditor::PushRedo( void )
  4713. {
  4714. Assert( m_bRedoPending );
  4715. m_bRedoPending = false;
  4716. // Copy current data
  4717. CSentence *r = new CSentence();
  4718. *r = m_Tags;
  4719. PEUndo *undo = m_UndoStack[ m_nUndoLevel - 1 ];
  4720. undo->redo = r;
  4721. }
  4722. void PhonemeEditor::WipeUndo( void )
  4723. {
  4724. while ( m_UndoStack.Size() > 0 )
  4725. {
  4726. PEUndo *u = m_UndoStack[ 0 ];
  4727. delete u->undo;
  4728. delete u->redo;
  4729. delete u;
  4730. m_UndoStack.Remove( 0 );
  4731. }
  4732. m_nUndoLevel = 0;
  4733. }
  4734. void PhonemeEditor::WipeRedo( void )
  4735. {
  4736. // Wipe everything above level
  4737. while ( m_UndoStack.Size() > m_nUndoLevel )
  4738. {
  4739. PEUndo *u = m_UndoStack[ m_nUndoLevel ];
  4740. delete u->undo;
  4741. delete u->redo;
  4742. delete u;
  4743. m_UndoStack.Remove( m_nUndoLevel );
  4744. }
  4745. }
  4746. //-----------------------------------------------------------------------------
  4747. // Purpose:
  4748. // Input : word -
  4749. // phoneme -
  4750. //-----------------------------------------------------------------------------
  4751. void PhonemeEditor::SetClickedPhoneme( int word, int phoneme )
  4752. {
  4753. m_nClickedPhoneme = phoneme;
  4754. m_nClickedWord = word;
  4755. }
  4756. //-----------------------------------------------------------------------------
  4757. // Purpose:
  4758. // Output : CPhonemeTag
  4759. //-----------------------------------------------------------------------------
  4760. CPhonemeTag *PhonemeEditor::GetClickedPhoneme( void )
  4761. {
  4762. if ( m_nClickedPhoneme < 0 || m_nClickedWord < 0 )
  4763. return NULL;
  4764. if ( m_nClickedWord >= m_Tags.m_Words.Size() )
  4765. return NULL;
  4766. CWordTag *word = m_Tags.m_Words[ m_nClickedWord ];
  4767. if ( !word )
  4768. return NULL;
  4769. if ( m_nClickedPhoneme >= word->m_Phonemes.Size() )
  4770. return NULL;
  4771. CPhonemeTag *phoneme = word->m_Phonemes[ m_nClickedPhoneme ];
  4772. return phoneme;
  4773. }
  4774. //-----------------------------------------------------------------------------
  4775. // Purpose:
  4776. // Output : CWordTag
  4777. //-----------------------------------------------------------------------------
  4778. CWordTag *PhonemeEditor::GetClickedWord( void )
  4779. {
  4780. if ( m_nClickedWord < 0 )
  4781. return NULL;
  4782. if ( m_nClickedWord >= m_Tags.m_Words.Size() )
  4783. return NULL;
  4784. CWordTag *word = m_Tags.m_Words[ m_nClickedWord ];
  4785. return word;
  4786. }
  4787. void PhonemeEditor::ShowContextMenu_Phonemes( int mx, int my )
  4788. {
  4789. CountSelected();
  4790. // Construct main
  4791. mxPopupMenu *pop = new mxPopupMenu();
  4792. if ( m_pWaveFile )
  4793. {
  4794. mxPopupMenu *play = new mxPopupMenu;
  4795. play->add( va( "Original" ), IDC_PHONEME_PLAY_ORIG );
  4796. play->add( va( "Edited" ), IDC_PLAY_EDITED );
  4797. if ( m_bSelectionActive )
  4798. {
  4799. play->add( va( "Selection" ), IDC_PLAY_EDITED_SELECTION );
  4800. }
  4801. pop->addMenu( "Play", play );
  4802. if ( sound->IsSoundPlaying( m_pMixer ) )
  4803. {
  4804. pop->add( va( "Cancel playback" ), IDC_CANCELPLAYBACK );
  4805. }
  4806. pop->addSeparator();
  4807. }
  4808. pop->add( va( "Load..." ), IDC_LOADWAVEFILE );
  4809. if ( m_pWaveFile )
  4810. {
  4811. pop->add( va( "Save" ), IDC_SAVE_LINGUISTIC );
  4812. }
  4813. if ( m_bSelectionActive )
  4814. {
  4815. pop->addSeparator();
  4816. pop->add( va( "Deselect" ), IDC_DESELECT );
  4817. }
  4818. if ( m_pWaveFile )
  4819. {
  4820. pop->addSeparator();
  4821. pop->add( va( "Redo Extraction" ), IDC_REDO_PHONEMEEXTRACTION );
  4822. if ( m_nSelectedWordCount < 1 || AreSelectedWordsContiguous() )
  4823. {
  4824. pop->add( va( "Redo Extraction of selected words" ), IDC_REDO_PHONEMEEXTRACTION_SELECTION );
  4825. }
  4826. }
  4827. if ( m_pWaveFile && m_TagsExt.m_Words.Size() )
  4828. {
  4829. pop->addSeparator();
  4830. pop->add( va( "Commit extraction" ) , IDC_COMMITEXTRACTED );
  4831. pop->add( va( "Clear extraction" ), IDC_CLEAREXTRACTED );
  4832. }
  4833. if ( m_nUndoLevel != 0 || m_nUndoLevel != m_UndoStack.Size() )
  4834. {
  4835. pop->addSeparator();
  4836. if ( m_nUndoLevel != 0 )
  4837. {
  4838. pop->add( va( "Undo" ), IDC_UNDO );
  4839. }
  4840. if ( m_nUndoLevel != m_UndoStack.Size() )
  4841. {
  4842. pop->add( va( "Redo" ), IDC_REDO );
  4843. }
  4844. pop->add( va( "Clear Undo Info" ), IDC_CLEARUNDO );
  4845. }
  4846. if ( m_Tags.m_Words.Size() > 0 )
  4847. {
  4848. pop->addSeparator();
  4849. pop->add( va( "Cleanup words/phonemes" ), IDC_CLEANUP );
  4850. }
  4851. // Show hierarchical options menu
  4852. {
  4853. mxPopupMenu *api = 0;
  4854. if ( DoesExtractorExistFor( SPEECH_API_SAPI ) )
  4855. {
  4856. api = new mxPopupMenu();
  4857. api->add( "Microsoft Speech API", IDC_API_SAPI );
  4858. if ( g_viewerSettings.speechapiindex == SPEECH_API_SAPI )
  4859. {
  4860. api->setChecked( IDC_API_SAPI, true );
  4861. }
  4862. }
  4863. if ( DoesExtractorExistFor( SPEECH_API_LIPSINC ) )
  4864. {
  4865. if ( !api )
  4866. api = new mxPopupMenu();
  4867. api->add( "Lipsinc Speech API", IDC_API_LIPSINC );
  4868. if ( g_viewerSettings.speechapiindex == SPEECH_API_LIPSINC )
  4869. {
  4870. api->setChecked( IDC_API_LIPSINC, true );
  4871. }
  4872. }
  4873. pop->addSeparator();
  4874. pop->addMenu( "Change Speech API", api );
  4875. }
  4876. // Import export menu
  4877. if ( m_pWaveFile )
  4878. {
  4879. pop->addSeparator();
  4880. if ( m_Tags.m_Words.Count() > 0 )
  4881. {
  4882. pop->add( "Export word data to " WORD_DATA_EXTENSION "...", IDC_EXPORT_SENTENCE );
  4883. }
  4884. pop->add( "Import word data from " WORD_DATA_EXTENSION "...", IDC_IMPORT_SENTENCE );
  4885. pop->add( va("%s Voice Duck", m_Tags.GetVoiceDuck() ? "Disable" : "Enable" ), IDC_TOGGLE_VOICEDUCK );
  4886. }
  4887. pop->popup( this, mx, my );
  4888. }
  4889. void PhonemeEditor::ShowContextMenu_Emphasis( int mx, int my )
  4890. {
  4891. Emphasis_CountSelected();
  4892. // Construct main
  4893. mxPopupMenu *pop = new mxPopupMenu();
  4894. pop->add( va( "Select All" ), IDC_EMPHASIS_SELECTALL );
  4895. if ( m_nNumSelected > 0 )
  4896. {
  4897. pop->add( va( "Deselect All" ), IDC_EMPHASIS_DESELECT );
  4898. }
  4899. if ( m_nUndoLevel != 0 || m_nUndoLevel != m_UndoStack.Size() )
  4900. {
  4901. pop->addSeparator();
  4902. if ( m_nUndoLevel != 0 )
  4903. {
  4904. pop->add( va( "Undo" ), IDC_UNDO );
  4905. }
  4906. if ( m_nUndoLevel != m_UndoStack.Size() )
  4907. {
  4908. pop->add( va( "Redo" ), IDC_REDO );
  4909. }
  4910. pop->add( va( "Clear Undo Info" ), IDC_CLEARUNDO );
  4911. }
  4912. pop->popup( this, mx, my );
  4913. }
  4914. void PhonemeEditor::ShowContextMenu( int mx, int my )
  4915. {
  4916. switch ( GetMode() )
  4917. {
  4918. default:
  4919. case MODE_PHONEMES:
  4920. ShowContextMenu_Phonemes( mx, my );
  4921. break;
  4922. case MODE_EMPHASIS:
  4923. ShowContextMenu_Emphasis( mx, my );
  4924. break;
  4925. }
  4926. }
  4927. void PhonemeEditor::ShiftSelectedPhoneme( int direction )
  4928. {
  4929. if ( GetMode() != MODE_PHONEMES )
  4930. return;
  4931. CountSelected();
  4932. switch ( m_nSelectedPhonemeCount )
  4933. {
  4934. case 1:
  4935. break;
  4936. case 0:
  4937. Con_Printf( "Can't shift phonemes, none selected\n" );
  4938. return;
  4939. default:
  4940. Con_Printf( "Can only shift one phoneme at a time via keyboard\n" );
  4941. return;
  4942. }
  4943. RECT rc;
  4944. GetWorkspaceRect( rc );
  4945. // Determine start/stop positions
  4946. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  4947. float endtime = w2() / GetPixelsPerSecond() + starttime;
  4948. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  4949. float movetime = timeperpixel * (float)direction;
  4950. float maxmove = ComputeMaxPhonemeShift( direction > 0 ? true : false, false );
  4951. if ( direction > 0 )
  4952. {
  4953. if ( movetime > maxmove )
  4954. {
  4955. movetime = maxmove;
  4956. Con_Printf( "Further shift is blocked on right\n" );
  4957. }
  4958. }
  4959. else
  4960. {
  4961. if ( movetime < -maxmove )
  4962. {
  4963. movetime = -maxmove;
  4964. Con_Printf( "Further shift is blocked on left\n" );
  4965. }
  4966. }
  4967. if ( fabs( movetime ) < 0.0001f )
  4968. return;
  4969. SetDirty( true );
  4970. PushUndo();
  4971. TraversePhonemes( &PhonemeEditor::ITER_MoveSelectedPhonemes, movetime );
  4972. PushRedo();
  4973. m_bWordsActive = false;
  4974. redraw();
  4975. Con_Printf( "Shift phoneme %s\n", direction == -1 ? "left" : "right" );
  4976. }
  4977. void PhonemeEditor::ExtendSelectedPhonemeEndTime( int direction )
  4978. {
  4979. if ( GetMode() != MODE_PHONEMES )
  4980. return;
  4981. CountSelected();
  4982. if ( m_nSelectedPhonemeCount != 1 )
  4983. return;
  4984. RECT rc;
  4985. GetWorkspaceRect( rc );
  4986. // Determine start/stop positions
  4987. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  4988. float endtime = w2() / GetPixelsPerSecond() + starttime;
  4989. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  4990. float movetime = timeperpixel * (float)direction;
  4991. SetDirty( true );
  4992. PushUndo();
  4993. TraversePhonemes( &PhonemeEditor::ITER_ExtendSelectedPhonemeEndTimes, movetime );
  4994. PushRedo();
  4995. m_bWordsActive = false;
  4996. redraw();
  4997. Con_Printf( "Extend phoneme end %s\n", direction == -1 ? "left" : "right" );
  4998. }
  4999. void PhonemeEditor::SelectNextPhoneme( int direction )
  5000. {
  5001. if ( GetMode() != MODE_PHONEMES )
  5002. return;
  5003. CountSelected();
  5004. if ( m_nSelectedPhonemeCount != 1 )
  5005. {
  5006. if ( m_nSelectedWordCount == 1 )
  5007. {
  5008. CWordTag *word = GetSelectedWord();
  5009. if ( word && word->m_Phonemes.Size() > 0 )
  5010. {
  5011. m_nSelectedPhonemeCount = 1;
  5012. CPhonemeTag *p = word->m_Phonemes[ direction ? word->m_Phonemes.Size() - 1 : 0 ];
  5013. p->m_bSelected = true;
  5014. }
  5015. else
  5016. {
  5017. return;
  5018. }
  5019. }
  5020. else
  5021. {
  5022. return;
  5023. }
  5024. }
  5025. Con_Printf( "Move to next phoneme %s\n", direction == -1 ? "left" : "right" );
  5026. for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
  5027. {
  5028. CWordTag *word = m_Tags.m_Words[ i ];
  5029. if ( !word )
  5030. continue;
  5031. for ( int j = 0; j < word->m_Phonemes.Size(); j++ )
  5032. {
  5033. CPhonemeTag *phoneme = word->m_Phonemes[ j ];
  5034. if ( !phoneme )
  5035. continue;
  5036. if ( !phoneme->m_bSelected )
  5037. continue;
  5038. // Deselect this one and move
  5039. int nextindex = j + direction;
  5040. if ( nextindex < 0 )
  5041. {
  5042. nextindex = word->m_Phonemes.Size() - 1;
  5043. }
  5044. else if ( nextindex >= word->m_Phonemes.Size() )
  5045. {
  5046. nextindex = 0;
  5047. }
  5048. phoneme->m_bSelected = false;
  5049. phoneme = word->m_Phonemes[ nextindex ];
  5050. phoneme->m_bSelected = true;
  5051. m_bWordsActive = false;
  5052. redraw();
  5053. return;
  5054. }
  5055. }
  5056. }
  5057. bool PhonemeEditor::IsPhonemeSelected( CWordTag *word )
  5058. {
  5059. for ( int i = 0 ; i < word->m_Phonemes.Size(); i++ )
  5060. {
  5061. CPhonemeTag *p = word->m_Phonemes[ i ];
  5062. if ( !p || !p->m_bSelected )
  5063. continue;
  5064. return true;
  5065. }
  5066. return false;
  5067. }
  5068. void PhonemeEditor::SelectNextWord( int direction )
  5069. {
  5070. if ( GetMode() != MODE_PHONEMES )
  5071. return;
  5072. CountSelected();
  5073. if ( m_nSelectedWordCount != 1 &&
  5074. m_nSelectedPhonemeCount != 1 )
  5075. {
  5076. // Selected first word then
  5077. if ( m_nSelectedWordCount == 0 && m_Tags.m_Words.Size() > 0 )
  5078. {
  5079. CWordTag *word = m_Tags.m_Words[ direction ? m_Tags.m_Words.Size() - 1 : 0 ];
  5080. word->m_bSelected = true;
  5081. m_nSelectedWordCount = 1;
  5082. }
  5083. else
  5084. {
  5085. return;
  5086. }
  5087. }
  5088. Con_Printf( "Move to next word %s\n", direction == -1 ? "left" : "right" );
  5089. for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
  5090. {
  5091. CWordTag *word = m_Tags.m_Words[ i ];
  5092. if ( !word )
  5093. continue;
  5094. if ( m_nSelectedWordCount == 1 )
  5095. {
  5096. if ( !word->m_bSelected )
  5097. continue;
  5098. }
  5099. else
  5100. {
  5101. if ( !IsPhonemeSelected( word ) )
  5102. continue;
  5103. }
  5104. // Deselect word
  5105. word->m_bSelected = false;
  5106. for ( int j = 0; j < word->m_Phonemes.Size(); j++ )
  5107. {
  5108. CPhonemeTag *phoneme = word->m_Phonemes[ j ];
  5109. if ( !phoneme )
  5110. continue;
  5111. if ( !phoneme->m_bSelected )
  5112. continue;
  5113. phoneme->m_bSelected = false;
  5114. }
  5115. // Deselect this one and move
  5116. int nextword = i + direction;
  5117. if ( nextword < 0 )
  5118. {
  5119. nextword = m_Tags.m_Words.Size() - 1;
  5120. }
  5121. else if ( nextword >= m_Tags.m_Words.Size() )
  5122. {
  5123. nextword = 0;
  5124. }
  5125. word = m_Tags.m_Words[ nextword ];
  5126. word->m_bSelected = true;
  5127. if ( word->m_Phonemes.Size() > 0 )
  5128. {
  5129. CPhonemeTag *phoneme = NULL;
  5130. if ( direction > 0 )
  5131. {
  5132. phoneme = word->m_Phonemes[ 0 ];
  5133. }
  5134. else
  5135. {
  5136. phoneme = word->m_Phonemes[ word->m_Phonemes.Size() - 1 ];
  5137. }
  5138. phoneme->m_bSelected = true;
  5139. }
  5140. m_bWordsActive = true;
  5141. redraw();
  5142. return;
  5143. }
  5144. }
  5145. void PhonemeEditor::ShiftSelectedWord( int direction )
  5146. {
  5147. if ( GetMode() != MODE_PHONEMES )
  5148. return;
  5149. CountSelected();
  5150. switch ( m_nSelectedWordCount )
  5151. {
  5152. case 1:
  5153. break;
  5154. case 0:
  5155. Con_Printf( "Can't shift words, none selected\n" );
  5156. return;
  5157. default:
  5158. Con_Printf( "Can only shift one word at a time via keyboard\n" );
  5159. return;
  5160. }
  5161. RECT rc;
  5162. GetWorkspaceRect( rc );
  5163. // Determine start/stop positions
  5164. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  5165. float endtime = w2() / GetPixelsPerSecond() + starttime;
  5166. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  5167. float movetime = timeperpixel * (float)direction;
  5168. float maxmove = ComputeMaxWordShift( direction > 0 ? true : false, false );
  5169. if ( direction > 0 )
  5170. {
  5171. if ( movetime > maxmove )
  5172. {
  5173. movetime = maxmove;
  5174. Con_Printf( "Further shift is blocked on right\n" );
  5175. }
  5176. }
  5177. else
  5178. {
  5179. if ( movetime < -maxmove )
  5180. {
  5181. movetime = -maxmove;
  5182. Con_Printf( "Further shift is blocked on left\n" );
  5183. }
  5184. }
  5185. if ( fabs( movetime ) < 0.0001f )
  5186. return;
  5187. SetDirty( true );
  5188. PushUndo();
  5189. TraverseWords( &PhonemeEditor::ITER_MoveSelectedWords, movetime );
  5190. PushRedo();
  5191. m_bWordsActive = true;
  5192. redraw();
  5193. Con_Printf( "Shift word %s\n", direction == -1 ? "left" : "right" );
  5194. }
  5195. void PhonemeEditor::ExtendSelectedWordEndTime( int direction )
  5196. {
  5197. if ( GetMode() != MODE_PHONEMES )
  5198. return;
  5199. CountSelected();
  5200. if ( m_nSelectedWordCount != 1 )
  5201. return;
  5202. RECT rc;
  5203. GetWorkspaceRect( rc );
  5204. // Determine start/stop positions
  5205. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  5206. float endtime = w2() / GetPixelsPerSecond() + starttime;
  5207. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  5208. float movetime = timeperpixel * (float)direction;
  5209. SetDirty( true );
  5210. PushUndo();
  5211. TraverseWords( &PhonemeEditor::ITER_ExtendSelectedWordEndTimes, movetime );
  5212. PushRedo();
  5213. m_bWordsActive = true;
  5214. redraw();
  5215. Con_Printf( "Extend word end %s\n", direction == -1 ? "left" : "right" );
  5216. }
  5217. //-----------------------------------------------------------------------------
  5218. // Purpose:
  5219. // Input : *word -
  5220. // Output : int
  5221. //-----------------------------------------------------------------------------
  5222. int PhonemeEditor::IndexOfWord( CWordTag *word )
  5223. {
  5224. for ( int i = 0 ; i < m_Tags.m_Words.Size(); i++ )
  5225. {
  5226. if ( m_Tags.m_Words[ i ] == word )
  5227. return i;
  5228. }
  5229. return -1;
  5230. }
  5231. //-----------------------------------------------------------------------------
  5232. // Purpose:
  5233. // Input : forward -
  5234. // *currentWord -
  5235. // **nextWord -
  5236. // Output : float
  5237. //-----------------------------------------------------------------------------
  5238. float PhonemeEditor::GetTimeGapToNextWord( bool forward, CWordTag *currentWord, CWordTag **ppNextWord /* = NULL */ )
  5239. {
  5240. if ( ppNextWord )
  5241. {
  5242. *ppNextWord = NULL;
  5243. }
  5244. if ( !currentWord )
  5245. return 0.0f;
  5246. int wordnum = IndexOfWord( currentWord );
  5247. if ( wordnum == -1 )
  5248. return 0.0f;
  5249. // Go in correct direction
  5250. int newwordnum = wordnum + ( forward ? 1 : -1 );
  5251. // There is no next word
  5252. if ( newwordnum >= m_Tags.m_Words.Size() )
  5253. {
  5254. return PLENTY_OF_TIME;
  5255. }
  5256. // There is no previous word
  5257. if ( newwordnum < 0 )
  5258. {
  5259. return PLENTY_OF_TIME;
  5260. }
  5261. if ( ppNextWord )
  5262. {
  5263. *ppNextWord = m_Tags.m_Words[ newwordnum ];
  5264. }
  5265. // Otherwise, figure out time gap
  5266. if ( forward )
  5267. {
  5268. float currentEnd = currentWord->m_flEndTime;
  5269. float nextStart = m_Tags.m_Words[ newwordnum ]->m_flStartTime;
  5270. return ( nextStart - currentEnd );
  5271. }
  5272. else
  5273. {
  5274. float previousEnd = m_Tags.m_Words[ newwordnum ]->m_flEndTime;
  5275. float currentStart = currentWord->m_flStartTime;
  5276. return ( currentStart - previousEnd );
  5277. }
  5278. Assert( 0 );
  5279. return 0.0f;
  5280. }
  5281. //-----------------------------------------------------------------------------
  5282. // Purpose:
  5283. // Input : forward -
  5284. // *currentPhoneme -
  5285. // **word -
  5286. // **phoneme -
  5287. // Output : float
  5288. //-----------------------------------------------------------------------------
  5289. float PhonemeEditor::GetTimeGapToNextPhoneme( bool forward, CPhonemeTag *currentPhoneme,
  5290. CWordTag **ppword /* = NULL */, CPhonemeTag **ppphoneme /* = NULL */ )
  5291. {
  5292. if ( ppword )
  5293. {
  5294. *ppword = NULL;
  5295. }
  5296. if ( ppphoneme )
  5297. {
  5298. *ppphoneme = NULL;
  5299. }
  5300. if ( !currentPhoneme )
  5301. return 0.0f;
  5302. CWordTag *word = m_Tags.GetWordForPhoneme( currentPhoneme );
  5303. if ( !word )
  5304. return 0.0f;
  5305. int wordnum = IndexOfWord( word );
  5306. Assert( wordnum != -1 );
  5307. int phonemenum = word->IndexOfPhoneme( currentPhoneme );
  5308. if ( phonemenum < 0 )
  5309. return 0.0f;
  5310. CPhonemeTag *nextPhoneme = NULL;
  5311. int nextphoneme = phonemenum + ( forward ? 1 : -1 );
  5312. // Try last phoneme of previous word
  5313. if ( nextphoneme < 0 )
  5314. {
  5315. wordnum--;
  5316. while ( wordnum >= 0 )
  5317. {
  5318. if ( ppword )
  5319. {
  5320. *ppword = m_Tags.m_Words[ wordnum ];
  5321. }
  5322. if ( m_Tags.m_Words.Size() > 0 )
  5323. {
  5324. if ( m_Tags.m_Words[ wordnum ]->m_Phonemes.Size() > 0 )
  5325. {
  5326. nextPhoneme = m_Tags.m_Words[ wordnum ]->m_Phonemes[ m_Tags.m_Words[ wordnum ]->m_Phonemes.Size() - 1 ];
  5327. break;
  5328. }
  5329. }
  5330. wordnum--;
  5331. }
  5332. }
  5333. // Try first phoneme of next word, if there is one
  5334. else if ( nextphoneme >= word->m_Phonemes.Size() )
  5335. {
  5336. wordnum++;
  5337. while ( wordnum < m_Tags.m_Words.Size() )
  5338. {
  5339. if ( ppword )
  5340. {
  5341. *ppword = m_Tags.m_Words[ wordnum ];
  5342. }
  5343. // Really it can't be zero, but check anyway
  5344. if ( m_Tags.m_Words.Size() > 0 )
  5345. {
  5346. if ( m_Tags.m_Words[ wordnum ]->m_Phonemes.Size() > 0 )
  5347. {
  5348. nextPhoneme = m_Tags.m_Words[ wordnum ]->m_Phonemes[ 0 ];
  5349. break;
  5350. }
  5351. }
  5352. wordnum++;
  5353. }
  5354. }
  5355. else
  5356. {
  5357. nextPhoneme = word->m_Phonemes[ nextphoneme ];
  5358. }
  5359. if ( !nextPhoneme )
  5360. return PLENTY_OF_TIME;
  5361. if ( ppphoneme )
  5362. {
  5363. *ppphoneme = nextPhoneme;
  5364. }
  5365. // Now compute time delta
  5366. float dt = 0.0f;
  5367. if ( forward )
  5368. {
  5369. dt = nextPhoneme->GetStartTime() - currentPhoneme->GetEndTime();
  5370. }
  5371. else
  5372. {
  5373. dt = currentPhoneme->GetStartTime() - nextPhoneme->GetEndTime();
  5374. }
  5375. return dt;
  5376. }
  5377. CPhonemeTag *PhonemeEditor::GetSelectedPhoneme( void )
  5378. {
  5379. CountSelected();
  5380. if ( m_nSelectedPhonemeCount != 1 )
  5381. return NULL;
  5382. for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
  5383. {
  5384. CWordTag *w = m_Tags.m_Words[ i ];
  5385. if ( !w )
  5386. continue;
  5387. for ( int j = 0; j < w->m_Phonemes.Size() ; j++ )
  5388. {
  5389. CPhonemeTag *p = w->m_Phonemes[ j ];
  5390. if ( !p || !p->m_bSelected )
  5391. continue;
  5392. return p;
  5393. }
  5394. }
  5395. return NULL;
  5396. }
  5397. CWordTag *PhonemeEditor::GetSelectedWord( void )
  5398. {
  5399. CountSelected();
  5400. if ( m_nSelectedWordCount != 1 )
  5401. return NULL;
  5402. for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
  5403. {
  5404. CWordTag *w = m_Tags.m_Words[ i ];
  5405. if ( !w || !w->m_bSelected )
  5406. continue;
  5407. return w;
  5408. }
  5409. return NULL;
  5410. }
  5411. void PhonemeEditor::OnMouseMove( mxEvent *event )
  5412. {
  5413. int mx = (short)event->x;
  5414. LimitDrag( mx );
  5415. event->x = (short)mx;
  5416. if ( m_nDragType != DRAGTYPE_NONE )
  5417. {
  5418. DrawFocusRect( "moving old" );
  5419. for ( int i = 0; i < m_FocusRects.Size(); i++ )
  5420. {
  5421. CFocusRect *f = &m_FocusRects[ i ];
  5422. f->m_rcFocus = f->m_rcOrig;
  5423. switch ( m_nDragType )
  5424. {
  5425. default:
  5426. {
  5427. // Only X Shifts supported
  5428. OffsetRect( &f->m_rcFocus, ( (short)event->x - m_nStartX ),
  5429. 0 );
  5430. }
  5431. break;
  5432. case DRAGTYPE_EMPHASIS_SELECT:
  5433. {
  5434. RECT rcWork;
  5435. GetWorkspaceRect( rcWork );
  5436. RECT rcEmphasis;
  5437. Emphasis_GetRect( rcWork, rcEmphasis );
  5438. RECT rcFocus;
  5439. rcFocus = f->m_rcOrig;
  5440. rcFocus.left = m_nStartX < (short)event->x ? m_nStartX : (short)event->x;
  5441. rcFocus.right = m_nStartX < (short)event->x ? (short)event->x : m_nStartX;
  5442. rcFocus.top = m_nStartY < (short)event->y ? m_nStartY : (short)event->y;
  5443. rcFocus.bottom = m_nStartY < (short)event->y ? (short)event->y : m_nStartY;
  5444. rcFocus.top = clamp( rcFocus.top, rcEmphasis.top, rcEmphasis.bottom );
  5445. rcFocus.bottom = clamp( rcFocus.bottom, rcEmphasis.top, rcEmphasis.bottom );
  5446. //OffsetRect( &rcFocus, 0, -rcEmphasis.top );
  5447. POINT offset;
  5448. offset.x = 0;
  5449. offset.y = 0;
  5450. ClientToScreen( (HWND)getHandle(), &offset );
  5451. OffsetRect( &rcFocus, offset.x, offset.y );
  5452. f->m_rcFocus = rcFocus;
  5453. }
  5454. break;
  5455. }
  5456. }
  5457. if ( m_nDragType == DRAGTYPE_EMPHASIS_MOVE )
  5458. {
  5459. redraw();
  5460. }
  5461. DrawFocusRect( "moving new" );
  5462. }
  5463. else
  5464. {
  5465. if ( m_hPrevCursor )
  5466. {
  5467. SetCursor( m_hPrevCursor );
  5468. m_hPrevCursor = NULL;
  5469. }
  5470. CountSelected();
  5471. int overhandle = IsMouseOverBoundary( event );
  5472. if ( overhandle == BOUNDARY_PHONEME && m_nSelectedPhonemeCount <= 1 )
  5473. {
  5474. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
  5475. }
  5476. else if ( overhandle == BOUNDARY_WORD && m_nSelectedWordCount <= 1 )
  5477. {
  5478. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
  5479. }
  5480. else if ( IsMouseOverSelection( (short)event->x, (short)event->y ) )
  5481. {
  5482. if ( IsMouseOverSelectionStartEdge( event ) )
  5483. {
  5484. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
  5485. }
  5486. else if ( IsMouseOverSelectionEndEdge( event ) )
  5487. {
  5488. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEWE ) );
  5489. }
  5490. else
  5491. {
  5492. if ( event->modifiers & mxEvent::KeyShift )
  5493. {
  5494. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
  5495. }
  5496. }
  5497. }
  5498. else
  5499. {
  5500. if ( IsMouseOverTag( (short)event->x, (short)event->y ) )
  5501. {
  5502. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
  5503. }
  5504. else
  5505. {
  5506. CPhonemeTag *pt = GetPhonemeTagUnderMouse( (short)event->x, (short)event->y );
  5507. CWordTag *wt = GetWordTagUnderMouse( (short)event->x, (short)event->y );
  5508. if ( wt || pt )
  5509. {
  5510. if ( pt )
  5511. {
  5512. // Select it
  5513. SelectExpression( pt );
  5514. }
  5515. if ( event->modifiers & mxEvent::KeyShift )
  5516. {
  5517. m_hPrevCursor = SetCursor( LoadCursor( NULL, IDC_SIZEALL ) );
  5518. }
  5519. }
  5520. }
  5521. }
  5522. }
  5523. switch ( m_nDragType )
  5524. {
  5525. default:
  5526. break;
  5527. case DRAGTYPE_EMPHASIS_MOVE:
  5528. {
  5529. Emphasis_MouseDrag( (short)event->x, (short)event->y );
  5530. m_Tags.Resort();
  5531. }
  5532. break;
  5533. case DRAGTYPE_SCRUBBER:
  5534. {
  5535. float t = GetTimeForPixel( (short)event->x );
  5536. t += m_flScrubberTimeOffset;
  5537. ClampTimeToSelectionInterval( t );
  5538. float dt = t - m_flScrub;
  5539. SetScrubTargetTime( t );
  5540. ScrubThink( dt, true );
  5541. SetScrubTime( t );
  5542. DrawScrubHandle();
  5543. }
  5544. break;
  5545. }
  5546. m_nLastX = (short)event->x;
  5547. m_nLastY = (short)event->y;
  5548. }
  5549. //-----------------------------------------------------------------------------
  5550. // Purpose:
  5551. //-----------------------------------------------------------------------------
  5552. void PhonemeEditor::EditInsertFirstPhonemeOfWord( void )
  5553. {
  5554. if ( GetMode() != MODE_PHONEMES )
  5555. return;
  5556. CWordTag *cw = GetSelectedWord();
  5557. if ( !cw )
  5558. return;
  5559. if ( cw->m_Phonemes.Size() != 0 )
  5560. {
  5561. Con_Printf( "Can't insert first phoneme into %s, already has phonemes\n", cw->GetWord() );
  5562. return;
  5563. }
  5564. CPhonemeParams params;
  5565. memset( &params, 0, sizeof( params ) );
  5566. strcpy( params.m_szDialogTitle, "Phoneme/Viseme Properties" );
  5567. strcpy( params.m_szName, "" );
  5568. params.m_nLeft = -1;
  5569. params.m_nTop = -1;
  5570. params.m_bPositionDialog = true;
  5571. params.m_bMultiplePhoneme = true;
  5572. if ( params.m_bPositionDialog )
  5573. {
  5574. RECT rcWord;
  5575. GetWordRect( cw, rcWord );
  5576. // Convert to screen coords
  5577. POINT pt;
  5578. pt.x = rcWord.left;
  5579. pt.y = rcWord.top;
  5580. ClientToScreen( (HWND)getHandle(), &pt );
  5581. params.m_nLeft = pt.x;
  5582. params.m_nTop = pt.y;
  5583. }
  5584. int iret = PhonemeProperties( &params );
  5585. SetFocus( (HWND)getHandle() );
  5586. if ( !iret )
  5587. {
  5588. return;
  5589. }
  5590. int phonemeCount = CSentence::CountWords( params.m_szName );
  5591. if ( phonemeCount <= 0 )
  5592. {
  5593. return;
  5594. }
  5595. float wordLength = cw->m_flEndTime - cw->m_flStartTime;
  5596. float timePerPhoneme = wordLength / (float)phonemeCount;
  5597. float currentTime = cw->m_flStartTime;
  5598. SetDirty( true );
  5599. PushUndo();
  5600. unsigned char *in;
  5601. char *out;
  5602. char phonemeName[ 128 ];
  5603. in = (unsigned char *)params.m_szName;
  5604. do
  5605. {
  5606. out = phonemeName;
  5607. while ( *in > 32 )
  5608. {
  5609. *out++ = *in++;
  5610. }
  5611. *out = 0;
  5612. CPhonemeTag phoneme;
  5613. phoneme.SetPhonemeCode( TextToPhoneme( phonemeName ) );
  5614. phoneme.SetTag( phonemeName );
  5615. phoneme.SetStartTime( currentTime );
  5616. phoneme.SetEndTime( currentTime + timePerPhoneme );
  5617. phoneme.m_bSelected = false;
  5618. cw->m_Phonemes.AddToTail( new CPhonemeTag( phoneme ) );
  5619. currentTime += timePerPhoneme;
  5620. if ( !*in )
  5621. break;
  5622. // Skip whitespace
  5623. in++;
  5624. } while ( 1 );
  5625. cw->m_Phonemes[ 0 ]->m_bSelected = true;
  5626. PushRedo();
  5627. // Add it
  5628. redraw();
  5629. }
  5630. void PhonemeEditor::SelectPhonemes( bool forward )
  5631. {
  5632. if ( GetMode() != MODE_PHONEMES )
  5633. return;
  5634. CountSelected();
  5635. if ( m_nSelectedPhonemeCount != 1 )
  5636. return;
  5637. CPhonemeTag *phoneme = GetSelectedPhoneme();
  5638. if ( !phoneme )
  5639. return;
  5640. // Figure out it's word and index
  5641. CWordTag *word = m_Tags.GetWordForPhoneme( phoneme );
  5642. if ( !word )
  5643. return;
  5644. int wordNum = IndexOfWord( word );
  5645. if ( wordNum == -1 )
  5646. return;
  5647. // Select remaining phonemes in current word
  5648. int i;
  5649. i = word->IndexOfPhoneme( phoneme );
  5650. if ( i == -1 )
  5651. return;
  5652. if ( forward )
  5653. {
  5654. // Start at next one
  5655. i++;
  5656. for ( ; i < word->m_Phonemes.Size(); i++ )
  5657. {
  5658. phoneme = word->m_Phonemes[ i ];
  5659. phoneme->m_bSelected = true;
  5660. }
  5661. // Now start at next word
  5662. wordNum++;
  5663. for ( ; wordNum < m_Tags.m_Words.Size(); wordNum++ )
  5664. {
  5665. word = m_Tags.m_Words[ wordNum ];
  5666. for ( int j = 0; j < word->m_Phonemes.Size(); j++ )
  5667. {
  5668. phoneme = word->m_Phonemes[ j ];
  5669. phoneme->m_bSelected = true;
  5670. }
  5671. }
  5672. }
  5673. else
  5674. {
  5675. // Start at previous
  5676. i--;
  5677. for ( ; i >= 0; i-- )
  5678. {
  5679. phoneme = word->m_Phonemes[ i ];
  5680. phoneme->m_bSelected = true;
  5681. }
  5682. // Now start at previous word
  5683. wordNum--;
  5684. for ( ; wordNum >= 0 ; wordNum-- )
  5685. {
  5686. word = m_Tags.m_Words[ wordNum ];
  5687. for ( int j = 0; j < word->m_Phonemes.Size(); j++ )
  5688. {
  5689. phoneme = word->m_Phonemes[ j ];
  5690. phoneme->m_bSelected = true;
  5691. }
  5692. }
  5693. }
  5694. redraw();
  5695. }
  5696. void PhonemeEditor::SelectWords( bool forward )
  5697. {
  5698. if ( GetMode() != MODE_PHONEMES )
  5699. return;
  5700. CountSelected();
  5701. if ( m_nSelectedWordCount != 1 )
  5702. return;
  5703. // Figure out it's word and index
  5704. CWordTag *word = GetSelectedWord();
  5705. if ( !word )
  5706. return;
  5707. int wordNum = IndexOfWord( word );
  5708. if ( wordNum == -1 )
  5709. return;
  5710. if ( forward )
  5711. {
  5712. wordNum++;
  5713. for ( ; wordNum < m_Tags.m_Words.Size(); wordNum++ )
  5714. {
  5715. word = m_Tags.m_Words[ wordNum ];
  5716. word->m_bSelected = true;
  5717. }
  5718. }
  5719. else
  5720. {
  5721. wordNum--;
  5722. for ( ; wordNum >= 0; wordNum-- )
  5723. {
  5724. word = m_Tags.m_Words[ wordNum ];
  5725. word->m_bSelected = true;
  5726. }
  5727. }
  5728. redraw();
  5729. }
  5730. bool PhonemeEditor::AreSelectedWordsContiguous( void )
  5731. {
  5732. CountSelected();
  5733. if ( m_nSelectedWordCount < 1 )
  5734. return false;
  5735. if ( m_nSelectedWordCount == 1 )
  5736. return true;
  5737. // Find first selected word
  5738. int runcount = 0;
  5739. bool parity = false;
  5740. for ( int i = 0 ; i < m_Tags.m_Words.Size() ; i++ )
  5741. {
  5742. CWordTag *word = m_Tags.m_Words[ i ];
  5743. if ( !word )
  5744. continue;
  5745. if ( word->m_bSelected )
  5746. {
  5747. if ( !parity )
  5748. {
  5749. parity = true;
  5750. runcount++;
  5751. }
  5752. }
  5753. else
  5754. {
  5755. if ( parity )
  5756. {
  5757. parity = false;
  5758. }
  5759. }
  5760. }
  5761. if ( runcount == 1 )
  5762. return true;
  5763. return false;
  5764. }
  5765. bool PhonemeEditor::AreSelectedPhonemesContiguous( void )
  5766. {
  5767. CountSelected();
  5768. if ( m_nSelectedPhonemeCount < 1 )
  5769. return false;
  5770. if ( m_nSelectedPhonemeCount == 1 )
  5771. return true;
  5772. // Find first selected word
  5773. int runcount = 0;
  5774. bool parity = false;
  5775. for ( int i = 0 ; i < m_Tags.m_Words.Size() ; i++ )
  5776. {
  5777. CWordTag *word = m_Tags.m_Words[ i ];
  5778. if ( !word )
  5779. continue;
  5780. for ( int j = 0 ; j < word->m_Phonemes.Size(); j++ )
  5781. {
  5782. CPhonemeTag *phoneme = word->m_Phonemes[ j ];
  5783. if ( !phoneme )
  5784. continue;
  5785. if ( phoneme->m_bSelected )
  5786. {
  5787. if ( !parity )
  5788. {
  5789. parity = true;
  5790. runcount++;
  5791. }
  5792. }
  5793. else
  5794. {
  5795. if ( parity )
  5796. {
  5797. parity = false;
  5798. }
  5799. }
  5800. }
  5801. }
  5802. if ( runcount == 1 )
  5803. return true;
  5804. return false;
  5805. }
  5806. void PhonemeEditor::SortWords( bool prepareundo )
  5807. {
  5808. if ( prepareundo )
  5809. {
  5810. SetDirty( true );
  5811. PushUndo();
  5812. }
  5813. // Just bubble sort by start time
  5814. int c = m_Tags.m_Words.Count();
  5815. int i;
  5816. // check for start > end
  5817. for ( i = 0; i < c; i++ )
  5818. {
  5819. CWordTag *p1 = m_Tags.m_Words[ i ];
  5820. if (p1->m_flStartTime > p1->m_flEndTime )
  5821. {
  5822. float swap = p1->m_flStartTime;
  5823. p1->m_flStartTime = p1->m_flEndTime;
  5824. p1->m_flEndTime = swap;
  5825. }
  5826. }
  5827. for ( i = 0; i < c; i++ )
  5828. {
  5829. for ( int j = i + 1; j < c; j++ )
  5830. {
  5831. CWordTag *p1 = m_Tags.m_Words[ i ];
  5832. CWordTag *p2 = m_Tags.m_Words[ j ];
  5833. if ( p1->m_flStartTime < p2->m_flStartTime )
  5834. continue;
  5835. // Swap them
  5836. m_Tags.m_Words[ i ] = p2;
  5837. m_Tags.m_Words[ j ] = p1;
  5838. }
  5839. }
  5840. if ( prepareundo )
  5841. {
  5842. PushRedo();
  5843. }
  5844. }
  5845. void PhonemeEditor::SortPhonemes( bool prepareundo )
  5846. {
  5847. if ( prepareundo )
  5848. {
  5849. SetDirty( true );
  5850. PushUndo();
  5851. }
  5852. // Just bubble sort by start time
  5853. int wc = m_Tags.m_Words.Count();
  5854. for ( int w = 0; w < wc; w++ )
  5855. {
  5856. CWordTag *word = m_Tags.m_Words[ w ];
  5857. Assert( word );
  5858. int c = word->m_Phonemes.Count();
  5859. int i;
  5860. // check for start > end
  5861. for ( i = 0; i < c; i++ )
  5862. {
  5863. CPhonemeTag *p1 = word->m_Phonemes[ i ];
  5864. if (p1->GetStartTime() > p1->GetEndTime() )
  5865. {
  5866. float swap = p1->GetStartTime();
  5867. p1->SetStartTime( p1->GetEndTime() );
  5868. p1->SetEndTime( swap );
  5869. }
  5870. }
  5871. for ( i = 0; i < c; i++ )
  5872. {
  5873. for ( int j = i + 1; j < c; j++ )
  5874. {
  5875. CPhonemeTag *p1 = word->m_Phonemes[ i ];
  5876. CPhonemeTag *p2 = word->m_Phonemes[ j ];
  5877. if ( p1->GetStartTime() < p2->GetStartTime() )
  5878. continue;
  5879. // Swap them
  5880. word->m_Phonemes[ i ] = p2;
  5881. word->m_Phonemes[ j ] = p1;
  5882. }
  5883. }
  5884. }
  5885. if ( prepareundo )
  5886. {
  5887. PushRedo();
  5888. }
  5889. }
  5890. void PhonemeEditor::CleanupWordsAndPhonemes( bool prepareundo )
  5891. {
  5892. if ( GetMode() != MODE_PHONEMES )
  5893. return;
  5894. // 2 pixel gap
  5895. float snap_epsilon = 2.49f / GetPixelsPerSecond();
  5896. if ( prepareundo )
  5897. {
  5898. SetDirty( true );
  5899. PushUndo();
  5900. }
  5901. SortWords( false );
  5902. SortPhonemes( false );
  5903. for ( int i = 0 ; i < m_Tags.m_Words.Size() ; i++ )
  5904. {
  5905. CWordTag *word = m_Tags.m_Words[ i ];
  5906. if ( !word )
  5907. continue;
  5908. CWordTag *next = NULL;
  5909. if ( i < m_Tags.m_Words.Size() - 1 )
  5910. {
  5911. next = m_Tags.m_Words[ i + 1 ];
  5912. }
  5913. if ( word && next )
  5914. {
  5915. // Check for words close enough
  5916. float eps = next->m_flStartTime - word->m_flEndTime;
  5917. if ( eps && eps <= snap_epsilon )
  5918. {
  5919. float t = (word->m_flEndTime + next->m_flStartTime) * 0.5;
  5920. word->m_flEndTime = t;
  5921. next->m_flStartTime = t;
  5922. }
  5923. }
  5924. for ( int j = 0 ; j < word->m_Phonemes.Size(); j++ )
  5925. {
  5926. CPhonemeTag *phoneme = word->m_Phonemes[ j ];
  5927. if ( !phoneme )
  5928. continue;
  5929. CPhonemeTag *next = NULL;
  5930. if ( j < word->m_Phonemes.Size() - 1 )
  5931. {
  5932. next = word->m_Phonemes[ j + 1 ];
  5933. }
  5934. if ( phoneme && next )
  5935. {
  5936. float eps = next->GetStartTime() - phoneme->GetEndTime();
  5937. if ( eps && eps <= snap_epsilon )
  5938. {
  5939. float t = (phoneme->GetEndTime() + next->GetStartTime() ) * 0.5;
  5940. phoneme->SetEndTime( t );
  5941. next->SetStartTime( t );
  5942. }
  5943. }
  5944. }
  5945. }
  5946. if ( prepareundo )
  5947. {
  5948. PushRedo();
  5949. }
  5950. // NOTE: Caller must call "redraw()" to get screen to update
  5951. }
  5952. void PhonemeEditor::RealignPhonemesToWords( bool prepareundo )
  5953. {
  5954. if ( GetMode() != MODE_PHONEMES )
  5955. return;
  5956. if ( prepareundo )
  5957. {
  5958. SetDirty( true );
  5959. PushUndo();
  5960. }
  5961. SortWords( false );
  5962. SortPhonemes( false );
  5963. for ( int i = 0 ; i < m_Tags.m_Words.Size() ; i++ )
  5964. {
  5965. CWordTag *word = m_Tags.m_Words[ i ];
  5966. if ( !word )
  5967. continue;
  5968. CWordTag *next = NULL;
  5969. if ( i < m_Tags.m_Words.Size() - 1 )
  5970. {
  5971. next = m_Tags.m_Words[ i + 1 ];
  5972. }
  5973. float word_dt = word->m_flEndTime - word->m_flStartTime;
  5974. CPhonemeTag *FirstPhoneme = word->m_Phonemes[ 0 ];
  5975. if ( !FirstPhoneme )
  5976. continue;
  5977. CPhonemeTag *LastPhoneme = word->m_Phonemes[ word->m_Phonemes.Size() - 1 ];
  5978. if ( !LastPhoneme )
  5979. continue;
  5980. float phoneme_dt = LastPhoneme->GetEndTime() - FirstPhoneme->GetStartTime();
  5981. float phoneme_shift = FirstPhoneme->GetStartTime();
  5982. for ( int j = 0 ; j < word->m_Phonemes.Size(); j++ )
  5983. {
  5984. CPhonemeTag *phoneme = word->m_Phonemes[ j ];
  5985. if ( !phoneme )
  5986. continue;
  5987. CPhonemeTag *next = NULL;
  5988. if ( j < word->m_Phonemes.Size() - 1 )
  5989. {
  5990. next = word->m_Phonemes[ j + 1 ];
  5991. }
  5992. if (j == 0)
  5993. {
  5994. float t = (phoneme->GetStartTime() - phoneme_shift ) * (word_dt / phoneme_dt) + word->m_flStartTime;
  5995. phoneme->SetStartTime( t );
  5996. }
  5997. float t = (phoneme->GetEndTime() - phoneme_shift ) * (word_dt / phoneme_dt) + word->m_flStartTime;
  5998. phoneme->SetEndTime( t );
  5999. if (next)
  6000. {
  6001. next->SetStartTime( t );
  6002. }
  6003. }
  6004. }
  6005. if ( prepareundo )
  6006. {
  6007. PushRedo();
  6008. }
  6009. // NOTE: Caller must call "redraw()" to get screen to update
  6010. }
  6011. void PhonemeEditor::RealignWordsToPhonemes( bool prepareundo )
  6012. {
  6013. if ( GetMode() != MODE_PHONEMES )
  6014. return;
  6015. if ( prepareundo )
  6016. {
  6017. SetDirty( true );
  6018. PushUndo();
  6019. }
  6020. SortWords( false );
  6021. SortPhonemes( false );
  6022. for ( int i = 0 ; i < m_Tags.m_Words.Size() ; i++ )
  6023. {
  6024. CWordTag *word = m_Tags.m_Words[ i ];
  6025. if ( !word )
  6026. continue;
  6027. CPhonemeTag *FirstPhoneme = word->m_Phonemes[ 0 ];
  6028. if ( !FirstPhoneme )
  6029. continue;
  6030. CPhonemeTag *LastPhoneme = word->m_Phonemes[ word->m_Phonemes.Size() - 1 ];
  6031. if ( !LastPhoneme )
  6032. continue;
  6033. word->m_flStartTime = FirstPhoneme->GetStartTime();
  6034. word->m_flEndTime = LastPhoneme->GetEndTime();
  6035. }
  6036. if ( prepareundo )
  6037. {
  6038. PushRedo();
  6039. }
  6040. // NOTE: Caller must call "redraw()" to get screen to update
  6041. }
  6042. float PhonemeEditor::ComputeMaxWordShift( bool forward, bool allowcrop )
  6043. {
  6044. // skipping selected words, figure out max time shift of words before they selection touches any
  6045. // unselected words
  6046. // if allowcrop is true, then the maximum extends up to end of next word
  6047. float maxshift = PLENTY_OF_TIME;
  6048. if ( forward )
  6049. {
  6050. for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
  6051. {
  6052. CWordTag *w1 = m_Tags.m_Words[ i ];
  6053. if ( !w1 || !w1->m_bSelected )
  6054. continue;
  6055. CWordTag *w2 = NULL;
  6056. for ( int search = i + 1; search < m_Tags.m_Words.Size() ; search++ )
  6057. {
  6058. CWordTag *check = m_Tags.m_Words[ search ];
  6059. if ( !check || check->m_bSelected )
  6060. continue;
  6061. w2 = check;
  6062. break;
  6063. }
  6064. if ( w2 )
  6065. {
  6066. float shift;
  6067. if ( allowcrop )
  6068. {
  6069. shift = w2->m_flEndTime - w1->m_flEndTime;
  6070. }
  6071. else
  6072. {
  6073. shift = w2->m_flStartTime - w1->m_flEndTime;
  6074. }
  6075. if ( shift < maxshift )
  6076. {
  6077. maxshift = shift;
  6078. }
  6079. }
  6080. }
  6081. }
  6082. else
  6083. {
  6084. for ( int i = m_Tags.m_Words.Size() -1; i >= 0; i-- )
  6085. {
  6086. CWordTag *w1 = m_Tags.m_Words[ i ];
  6087. if ( !w1 || !w1->m_bSelected )
  6088. continue;
  6089. CWordTag *w2 = NULL;
  6090. for ( int search = i - 1; search >= 0 ; search-- )
  6091. {
  6092. CWordTag *check = m_Tags.m_Words[ search ];
  6093. if ( !check || check->m_bSelected )
  6094. continue;
  6095. w2 = check;
  6096. break;
  6097. }
  6098. if ( w2 )
  6099. {
  6100. float shift;
  6101. if ( allowcrop )
  6102. {
  6103. shift = w1->m_flStartTime - w2->m_flStartTime;
  6104. }
  6105. else
  6106. {
  6107. shift = w1->m_flStartTime - w2->m_flEndTime;
  6108. }
  6109. if ( shift < maxshift )
  6110. {
  6111. maxshift = shift;
  6112. }
  6113. }
  6114. }
  6115. }
  6116. return maxshift;
  6117. }
  6118. float PhonemeEditor::ComputeMaxPhonemeShift( bool forward, bool allowcrop )
  6119. {
  6120. // skipping selected phonemes, figure out max time shift of phonemes before they selection touches any
  6121. // unselected words
  6122. // if allowcrop is true, then the maximum extends up to end of next word
  6123. float maxshift = PLENTY_OF_TIME;
  6124. if ( forward )
  6125. {
  6126. for ( int i = 0; i < m_Tags.m_Words.Size(); i++ )
  6127. {
  6128. CWordTag *word = m_Tags.m_Words[ i ];
  6129. if ( !word )
  6130. continue;
  6131. for ( int j = 0; j < word->m_Phonemes.Size(); j++ )
  6132. {
  6133. CPhonemeTag *p1 = word->m_Phonemes[ j ];
  6134. if ( !p1 || !p1->m_bSelected )
  6135. continue;
  6136. // Find next unselected phoneme
  6137. CPhonemeTag *p2 = NULL;
  6138. CPhonemeTag *start = p1;
  6139. do
  6140. {
  6141. CPhonemeTag *test = NULL;
  6142. GetTimeGapToNextPhoneme( forward, start, NULL, &test );
  6143. if ( !test )
  6144. break;
  6145. if ( test->m_bSelected )
  6146. {
  6147. start = test;
  6148. continue;
  6149. }
  6150. p2 = test;
  6151. break;
  6152. } while ( 1 );
  6153. if ( p2 )
  6154. {
  6155. float shift;
  6156. if ( allowcrop )
  6157. {
  6158. shift = p2->GetEndTime() - p1->GetEndTime();
  6159. }
  6160. else
  6161. {
  6162. shift = p2->GetStartTime() - p1->GetEndTime();
  6163. }
  6164. if ( shift < maxshift )
  6165. {
  6166. maxshift = shift;
  6167. }
  6168. }
  6169. }
  6170. }
  6171. }
  6172. else
  6173. {
  6174. for ( int i = m_Tags.m_Words.Size() -1; i >= 0; i-- )
  6175. {
  6176. CWordTag *word = m_Tags.m_Words[ i ];
  6177. if ( !word )
  6178. continue;
  6179. for ( int j = word->m_Phonemes.Size() - 1; j >= 0; j-- )
  6180. {
  6181. CPhonemeTag *p1 = word->m_Phonemes[ j ];
  6182. if ( !p1 || !p1->m_bSelected )
  6183. continue;
  6184. // Find previous unselected phoneme
  6185. CPhonemeTag *p2 = NULL;
  6186. CPhonemeTag *start = p1;
  6187. do
  6188. {
  6189. CPhonemeTag *test = NULL;
  6190. GetTimeGapToNextPhoneme( forward, start, NULL, &test );
  6191. if ( !test )
  6192. break;
  6193. if ( test->m_bSelected )
  6194. {
  6195. start = test;
  6196. continue;
  6197. }
  6198. p2 = test;
  6199. break;
  6200. } while ( 1 );
  6201. if ( p2 )
  6202. {
  6203. float shift;
  6204. if ( allowcrop )
  6205. {
  6206. shift = p1->GetStartTime() - p2->GetStartTime();
  6207. }
  6208. else
  6209. {
  6210. shift = p1->GetStartTime() - p2->GetEndTime();
  6211. }
  6212. if ( shift < maxshift )
  6213. {
  6214. maxshift = shift;
  6215. }
  6216. }
  6217. }
  6218. }
  6219. }
  6220. return maxshift;
  6221. }
  6222. int PhonemeEditor::PixelsForDeltaTime( float dt )
  6223. {
  6224. if ( !dt )
  6225. return 0;
  6226. RECT rc;
  6227. GetWorkspaceRect( rc );
  6228. float starttime = m_nLeftOffset / GetPixelsPerSecond();
  6229. float endtime = w2() / GetPixelsPerSecond() + starttime;
  6230. float timeperpixel = ( endtime - starttime ) / (float)( rc.right - rc.left );
  6231. float pixels = dt / timeperpixel;
  6232. return abs( (int)pixels );
  6233. }
  6234. void PhonemeEditor::ClearDragLimit( void )
  6235. {
  6236. m_bLimitDrag = false;
  6237. m_nLeftLimit = -1;
  6238. m_nRightLimit = -1;
  6239. }
  6240. void PhonemeEditor::SetDragLimit( int dragtype )
  6241. {
  6242. ClearDragLimit();
  6243. float nextW, nextP;
  6244. float prevW, prevP;
  6245. nextW = ComputeMaxWordShift( true, false );
  6246. prevW = ComputeMaxWordShift( false, false );
  6247. nextP = ComputeMaxPhonemeShift( true, false );
  6248. prevP = ComputeMaxPhonemeShift( false, false );
  6249. /*
  6250. Con_Printf( "+w %f -w %f +p %f -p %f\n",
  6251. 1000.0f * nextW,
  6252. 1000.0f * prevW,
  6253. 1000.0f * nextP,
  6254. 1000.0f * prevP );
  6255. */
  6256. switch ( dragtype )
  6257. {
  6258. case DRAGTYPE_MOVEWORD:
  6259. m_bLimitDrag = true;
  6260. m_nLeftLimit = PixelsForDeltaTime( prevW );
  6261. m_nRightLimit = PixelsForDeltaTime( nextW );
  6262. break;
  6263. case DRAGTYPE_MOVEPHONEME:
  6264. m_bLimitDrag = true;
  6265. m_nLeftLimit = PixelsForDeltaTime( prevP );
  6266. m_nRightLimit = PixelsForDeltaTime( nextP );
  6267. break;
  6268. default:
  6269. ClearDragLimit();
  6270. break;
  6271. }
  6272. }
  6273. void PhonemeEditor::LimitDrag( int& mousex )
  6274. {
  6275. if ( m_nDragType == DRAGTYPE_NONE )
  6276. return;
  6277. if ( !m_bLimitDrag )
  6278. return;
  6279. int delta = mousex - m_nStartX;
  6280. if ( delta > 0 )
  6281. {
  6282. if ( m_nRightLimit >= 0 )
  6283. {
  6284. if ( delta > m_nRightLimit )
  6285. {
  6286. mousex = m_nStartX + m_nRightLimit;
  6287. }
  6288. }
  6289. }
  6290. else if ( delta < 0 )
  6291. {
  6292. if ( m_nLeftLimit >= 0 )
  6293. {
  6294. if ( abs( delta ) > abs( m_nLeftLimit ) )
  6295. {
  6296. mousex = m_nStartX - m_nLeftLimit;
  6297. }
  6298. }
  6299. }
  6300. }
  6301. //-----------------------------------------------------------------------------
  6302. // Purpose: Wipe undo/redo data
  6303. //-----------------------------------------------------------------------------
  6304. void PhonemeEditor::ClearUndo( void )
  6305. {
  6306. WipeUndo();
  6307. WipeRedo();
  6308. SetDirty( false );
  6309. }
  6310. //-----------------------------------------------------------------------------
  6311. // Purpose:
  6312. // Input : *tag -
  6313. //-----------------------------------------------------------------------------
  6314. void PhonemeEditor::SelectExpression( CPhonemeTag *tag )
  6315. {
  6316. if ( !models->GetActiveStudioModel() )
  6317. return;
  6318. CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr();
  6319. if ( !hdr )
  6320. return;
  6321. // Make sure phonemes are loaded
  6322. FacePoser_EnsurePhonemesLoaded();
  6323. CExpClass *cl = expressions->FindClass( "phonemes", true );
  6324. if ( !cl )
  6325. {
  6326. Con_Printf( "Couldn't load expressions/phonemes.txt!\n" );
  6327. return;
  6328. }
  6329. if ( expressions->GetActiveClass() != cl )
  6330. {
  6331. expressions->ActivateExpressionClass( cl );
  6332. }
  6333. CExpression *exp = cl->FindExpression( ConvertPhoneme( tag->GetPhonemeCode() ) );
  6334. if ( !exp )
  6335. {
  6336. Con_Printf( "Couldn't find phoneme '%s'\n", ConvertPhoneme( tag->GetPhonemeCode() ) );
  6337. return;
  6338. }
  6339. float *settings = exp->GetSettings();
  6340. for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
  6341. {
  6342. int j = hdr->pFlexcontroller( i )->localToGlobal;
  6343. models->GetActiveStudioModel()->SetFlexController( i, settings[j] );
  6344. }
  6345. }
  6346. void PhonemeEditor::OnSAPI( void )
  6347. {
  6348. if ( GetMode() != MODE_PHONEMES )
  6349. return;
  6350. g_viewerSettings.speechapiindex = SPEECH_API_SAPI;
  6351. m_pPhonemeExtractor = NULL;
  6352. CheckSpeechAPI();
  6353. redraw();
  6354. }
  6355. void PhonemeEditor::OnLipSinc( void )
  6356. {
  6357. if ( GetMode() != MODE_PHONEMES )
  6358. return;
  6359. g_viewerSettings.speechapiindex = SPEECH_API_LIPSINC;
  6360. m_pPhonemeExtractor = NULL;
  6361. CheckSpeechAPI();
  6362. redraw();
  6363. }
  6364. void PhonemeEditor::LoadPhonemeConverters()
  6365. {
  6366. m_pPhonemeExtractor = NULL;
  6367. // Enumerate modules under bin folder of exe
  6368. FileFindHandle_t findHandle;
  6369. const char *pFilename = filesystem->FindFirstEx( "phonemeextractors/*.dll", "EXECUTABLE_PATH", &findHandle );
  6370. while( pFilename )
  6371. {
  6372. char fullpath[ 512 ];
  6373. Q_snprintf( fullpath, sizeof( fullpath ), "phonemeextractors/%s", pFilename );
  6374. Con_Printf( "Loading extractor from %s\n", fullpath );
  6375. Extractor e;
  6376. e.module = Sys_LoadModule( fullpath );
  6377. if ( !e.module )
  6378. {
  6379. pFilename = filesystem->FindNext( findHandle );
  6380. continue;
  6381. }
  6382. CreateInterfaceFn factory = Sys_GetFactory( e.module );
  6383. if ( !factory )
  6384. {
  6385. pFilename = filesystem->FindNext( findHandle );
  6386. continue;
  6387. }
  6388. e.extractor = ( IPhonemeExtractor * )factory( VPHONEME_EXTRACTOR_INTERFACE, NULL );
  6389. if ( !e.extractor )
  6390. {
  6391. Warning( "Unable to get IPhonemeExtractor interface version %s from %s\n", VPHONEME_EXTRACTOR_INTERFACE, fullpath );
  6392. pFilename = filesystem->FindNext( findHandle );
  6393. continue;
  6394. }
  6395. e.apitype = e.extractor->GetAPIType();
  6396. g_Extractors.AddToTail( e );
  6397. pFilename = filesystem->FindNext( findHandle );
  6398. }
  6399. filesystem->FindClose( findHandle );
  6400. }
  6401. void PhonemeEditor::ValidateSpeechAPIIndex()
  6402. {
  6403. if ( !DoesExtractorExistFor( (PE_APITYPE)g_viewerSettings.speechapiindex ) )
  6404. {
  6405. if ( g_Extractors.Count() > 0 )
  6406. g_viewerSettings.speechapiindex = g_Extractors[0].apitype;
  6407. }
  6408. }
  6409. void PhonemeEditor::UnloadPhonemeConverters()
  6410. {
  6411. int c = g_Extractors.Count();
  6412. for ( int i = c - 1; i >= 0; i-- )
  6413. {
  6414. Extractor *e = &g_Extractors[ i ];
  6415. Sys_UnloadModule( e->module );
  6416. }
  6417. g_Extractors.RemoveAll();
  6418. m_pPhonemeExtractor = NULL;
  6419. }
  6420. bool PhonemeEditor::CheckSpeechAPI( void )
  6421. {
  6422. if ( GetMode() != MODE_PHONEMES )
  6423. {
  6424. return false;
  6425. }
  6426. if ( !m_pPhonemeExtractor )
  6427. {
  6428. int c = g_Extractors.Count();
  6429. for ( int i = 0; i < c; i++ )
  6430. {
  6431. Extractor *e = &g_Extractors[ i ];
  6432. if ( e->apitype == (PE_APITYPE)g_viewerSettings.speechapiindex )
  6433. {
  6434. m_pPhonemeExtractor = e->extractor;
  6435. break;
  6436. }
  6437. }
  6438. if ( !m_pPhonemeExtractor )
  6439. {
  6440. Con_ErrorPrintf( "Couldn't find phoneme extractor %i\n",
  6441. g_viewerSettings.speechapiindex );
  6442. }
  6443. }
  6444. return m_pPhonemeExtractor != NULL;
  6445. }
  6446. //-----------------------------------------------------------------------------
  6447. // Purpose:
  6448. // Output : char const
  6449. //-----------------------------------------------------------------------------
  6450. char const *PhonemeEditor::GetSpeechAPIName( void )
  6451. {
  6452. CheckSpeechAPI();
  6453. if ( m_pPhonemeExtractor )
  6454. {
  6455. return m_pPhonemeExtractor->GetName();
  6456. }
  6457. return "Unknown Speech API";
  6458. }
  6459. //-----------------------------------------------------------------------------
  6460. // Purpose:
  6461. // Output : Returns true on success, false on failure.
  6462. //-----------------------------------------------------------------------------
  6463. bool PhonemeEditor::PaintBackground( void )
  6464. {
  6465. redraw();
  6466. return false;
  6467. }
  6468. //-----------------------------------------------------------------------------
  6469. // Purpose:
  6470. // Output : PhonemeEditor::EditorMode
  6471. //-----------------------------------------------------------------------------
  6472. PhonemeEditor::EditorMode PhonemeEditor::GetMode( void ) const
  6473. {
  6474. return m_CurrentMode;
  6475. }
  6476. //-----------------------------------------------------------------------------
  6477. // Purpose:
  6478. // Input : rcWorkSpace -
  6479. // rcEmphasis -
  6480. //-----------------------------------------------------------------------------
  6481. void PhonemeEditor::Emphasis_GetRect( RECT const & rcWorkSpace, RECT& rcEmphasis )
  6482. {
  6483. rcEmphasis = rcWorkSpace;
  6484. int ybottom = rcWorkSpace.bottom - 2 * m_nTickHeight - 2;
  6485. int workspaceheight = rcWorkSpace.bottom - rcWorkSpace.top;
  6486. // Just past midpoint
  6487. rcEmphasis.top = rcWorkSpace.top + workspaceheight / 2 + 2;
  6488. // 60 units or
  6489. rcEmphasis.bottom = clamp( rcEmphasis.top + 60, rcEmphasis.top + 20, ybottom );
  6490. }
  6491. //-----------------------------------------------------------------------------
  6492. // Purpose:
  6493. //-----------------------------------------------------------------------------
  6494. void PhonemeEditor::OnModeChanged( void )
  6495. {
  6496. // Show/hide controls as necessary
  6497. }
  6498. //-----------------------------------------------------------------------------
  6499. // Purpose:
  6500. // Input : *parent -
  6501. //-----------------------------------------------------------------------------
  6502. void PhonemeEditor::Emphasis_Init( void )
  6503. {
  6504. m_nNumSelected = 0;
  6505. }
  6506. CEmphasisSample *PhonemeEditor::Emphasis_GetSampleUnderMouse( mxEvent *event )
  6507. {
  6508. if ( GetMode() != MODE_EMPHASIS )
  6509. return NULL;
  6510. if ( !m_pWaveFile )
  6511. return NULL;
  6512. if ( w2() <= 0 )
  6513. return NULL;
  6514. if ( GetPixelsPerSecond() <= 0 )
  6515. return NULL;
  6516. float timeperpixel = 1.0f / GetPixelsPerSecond();
  6517. float closest_dist = 999999.0f;
  6518. CEmphasisSample *bestsample = NULL;
  6519. int samples = m_Tags.GetNumSamples();
  6520. float clickTime = GetTimeForPixel( (short)event->x );
  6521. for ( int i = 0; i < samples; i++ )
  6522. {
  6523. CEmphasisSample *sample = m_Tags.GetSample( i );
  6524. float dist = fabs( sample->time - clickTime );
  6525. if ( dist < closest_dist )
  6526. {
  6527. bestsample = sample;
  6528. closest_dist = dist;
  6529. }
  6530. }
  6531. // Not close to any of them!!!
  6532. if ( closest_dist > ( 5.0f * timeperpixel ) )
  6533. {
  6534. return NULL;
  6535. }
  6536. return bestsample;
  6537. }
  6538. //-----------------------------------------------------------------------------
  6539. // Purpose:
  6540. //-----------------------------------------------------------------------------
  6541. void PhonemeEditor::Emphasis_DeselectAll( void )
  6542. {
  6543. if ( GetMode() != MODE_EMPHASIS )
  6544. return;
  6545. for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
  6546. {
  6547. CEmphasisSample *sample = m_Tags.GetSample( i );
  6548. sample->selected = false;
  6549. }
  6550. redraw();
  6551. }
  6552. //-----------------------------------------------------------------------------
  6553. // Purpose:
  6554. //-----------------------------------------------------------------------------
  6555. void PhonemeEditor::Emphasis_SelectAll( void )
  6556. {
  6557. if ( GetMode() != MODE_EMPHASIS )
  6558. return;
  6559. for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
  6560. {
  6561. CEmphasisSample *sample = m_Tags.GetSample( i );
  6562. sample->selected = true;
  6563. }
  6564. redraw();
  6565. }
  6566. //-----------------------------------------------------------------------------
  6567. // Purpose:
  6568. //-----------------------------------------------------------------------------
  6569. void PhonemeEditor::Emphasis_Delete( void )
  6570. {
  6571. if ( GetMode() != MODE_EMPHASIS )
  6572. return;
  6573. SetDirty( true );
  6574. PushUndo();
  6575. for ( int i = m_Tags.GetNumSamples() - 1; i >= 0 ; i-- )
  6576. {
  6577. CEmphasisSample *sample = m_Tags.GetSample( i );
  6578. if ( !sample->selected )
  6579. continue;
  6580. m_Tags.m_EmphasisSamples.Remove( i );
  6581. SetDirty( true );
  6582. }
  6583. PushRedo();
  6584. redraw();
  6585. }
  6586. //-----------------------------------------------------------------------------
  6587. // Purpose:
  6588. // Input : sample -
  6589. //-----------------------------------------------------------------------------
  6590. void PhonemeEditor::Emphasis_AddSample( CEmphasisSample const& sample )
  6591. {
  6592. if ( GetMode() != MODE_EMPHASIS )
  6593. return;
  6594. SetDirty( true );
  6595. PushUndo();
  6596. m_Tags.m_EmphasisSamples.AddToTail( sample );
  6597. m_Tags.Resort();
  6598. PushRedo();
  6599. redraw();
  6600. }
  6601. //-----------------------------------------------------------------------------
  6602. // Purpose:
  6603. //-----------------------------------------------------------------------------
  6604. void PhonemeEditor::Emphasis_CountSelected( void )
  6605. {
  6606. m_nNumSelected = 0;
  6607. for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
  6608. {
  6609. CEmphasisSample *sample = m_Tags.GetSample( i );
  6610. if ( !sample || !sample->selected )
  6611. continue;
  6612. m_nNumSelected++;
  6613. }
  6614. }
  6615. void PhonemeEditor::Emphasis_ShowContextMenu( mxEvent *event )
  6616. {
  6617. if ( GetMode() != MODE_EMPHASIS )
  6618. return;
  6619. CountSelected();
  6620. // Construct main menu
  6621. mxPopupMenu *pop = new mxPopupMenu();
  6622. if ( m_nNumSelected > 0 )
  6623. {
  6624. pop->add( va( "Delete" ), IDC_EMPHASIS_DELETE );
  6625. pop->add( "Deselect all", IDC_EMPHASIS_DESELECT );
  6626. }
  6627. pop->add( "Select all", IDC_EMPHASIS_SELECTALL );
  6628. pop->popup( this, (short)event->x, (short)event->y );
  6629. }
  6630. void PhonemeEditor::Emphasis_MouseDrag( int x, int y )
  6631. {
  6632. if ( m_nDragType != DRAGTYPE_EMPHASIS_MOVE )
  6633. return;
  6634. RECT rcWork;
  6635. GetWorkspaceRect( rcWork );
  6636. RECT rc;
  6637. Emphasis_GetRect( rcWork, rc );
  6638. int height = rc.bottom - rc.top;
  6639. int dx = x - m_nLastX;
  6640. int dy = y - m_nLastY;
  6641. float dfdx = (float)dx * GetTimePerPixel();
  6642. float dfdy = (float)dy / (float)height;
  6643. for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
  6644. {
  6645. CEmphasisSample *sample = m_Tags.GetSample( i );
  6646. if ( !sample || !sample->selected )
  6647. continue;
  6648. sample->time += dfdx;
  6649. //sample->time = clamp( sample->time, 0.0f, 1.0f );
  6650. sample->value -= dfdy;
  6651. sample->value = clamp( sample->value, 0.0f, 1.0f );
  6652. }
  6653. }
  6654. void PhonemeEditor::Emphasis_Redraw( CChoreoWidgetDrawHelper& drawHelper, RECT& rcWorkSpace )
  6655. {
  6656. if ( GetMode() != MODE_EMPHASIS &&
  6657. GetMode() != MODE_PHONEMES )
  6658. return;
  6659. bool fullmode = GetMode() == MODE_EMPHASIS;
  6660. RECT rcClient;
  6661. Emphasis_GetRect( rcWorkSpace, rcClient );
  6662. RECT rcText;
  6663. rcText = rcClient;
  6664. InflateRect( &rcText, -15, 0 );
  6665. OffsetRect( &rcText, 0, -20 );
  6666. rcText.bottom = rcText.top + 20;
  6667. if ( fullmode )
  6668. {
  6669. drawHelper.DrawColoredText( "Arial", 15, FW_BOLD, PEColor( COLOR_PHONEME_EMPHASIS_TEXT ), rcText, "Emphasis..." );
  6670. }
  6671. {
  6672. int h = rcClient.bottom - rcClient.top;
  6673. int offset = h/3;
  6674. RECT rcSpot = rcClient;
  6675. rcSpot.bottom = rcSpot.top + offset;
  6676. drawHelper.DrawGradientFilledRect(
  6677. rcSpot,
  6678. PEColor( COLOR_PHONEME_EMPHASIS_BG_STRONG ),
  6679. PEColor( COLOR_PHONEME_EMPHASIS_BG ),
  6680. true );
  6681. OffsetRect( &rcSpot, 0, offset );
  6682. drawHelper.DrawFilledRect( PEColor( COLOR_PHONEME_EMPHASIS_BG ), rcSpot );
  6683. OffsetRect( &rcSpot, 0, offset );
  6684. drawHelper.DrawGradientFilledRect(
  6685. rcSpot,
  6686. PEColor( COLOR_PHONEME_EMPHASIS_BG ),
  6687. PEColor( COLOR_PHONEME_EMPHASIS_BG_WEAK ),
  6688. true );
  6689. }
  6690. COLORREF gray = PEColor( COLOR_PHONEME_EMPHASIS_MIDLINE );
  6691. drawHelper.DrawOutlinedRect( PEColor( COLOR_PHONEME_EMPHASIS_BORDER ), PS_SOLID, 1, rcClient );
  6692. COLORREF lineColor = PEColor( COLOR_PHONEME_EMPHASIS_LINECOLOR );
  6693. COLORREF dotColor = PEColor( COLOR_PHONEME_EMPHASIS_DOTCOLOR );
  6694. COLORREF dotColorSelected = PEColor( COLOR_PHONEME_EMPHASIS_DOTCOLOR_SELECTED );
  6695. int midy = ( rcClient.bottom + rcClient.top ) / 2;
  6696. drawHelper.DrawColoredLine( gray, PS_SOLID, 1, rcClient.left, midy,
  6697. rcClient.right, midy );
  6698. int height = rcClient.bottom - rcClient.top;
  6699. int bottom = rcClient.bottom - 1;
  6700. if ( !m_pWaveFile )
  6701. return;
  6702. float running_length = m_pWaveFile->GetRunningLength();
  6703. // FIXME: adjust this based on framerate....
  6704. float timeperpixel = GetTimePerPixel();
  6705. float starttime, endtime;
  6706. GetScreenStartAndEndTime( starttime, endtime );
  6707. int prevx = 0;
  6708. float prev_t = starttime;
  6709. float prev_value = m_Tags.GetIntensity( prev_t, running_length );
  6710. int dx = 5;
  6711. for ( int x = 0; x < ( w2() + dx ); x += dx )
  6712. {
  6713. float t = GetTimeForPixel( x );
  6714. float value = m_Tags.GetIntensity( t, running_length );
  6715. // Draw segment
  6716. drawHelper.DrawColoredLine( lineColor, PS_SOLID, 1,
  6717. prevx,
  6718. bottom - prev_value * height,
  6719. x,
  6720. bottom - value * height );
  6721. prev_t = t;
  6722. prev_value = value;
  6723. prevx = x;
  6724. }
  6725. int numsamples = m_Tags.GetNumSamples();
  6726. for ( int sample = 0; sample < numsamples; sample++ )
  6727. {
  6728. CEmphasisSample *start = m_Tags.GetSample( sample );
  6729. int x = ( start->time - starttime ) / timeperpixel;
  6730. float value = m_Tags.GetIntensity( start->time, running_length );
  6731. int y = bottom - value * height;
  6732. int dotsize = 4;
  6733. int dotSizeSelected = 5;
  6734. COLORREF clr = dotColor;
  6735. COLORREF clrSelected = dotColorSelected;
  6736. drawHelper.DrawCircle(
  6737. start->selected ? clrSelected : clr,
  6738. x, y,
  6739. start->selected ? dotSizeSelected : dotsize,
  6740. true );
  6741. }
  6742. }
  6743. //-----------------------------------------------------------------------------
  6744. // Purpose:
  6745. // Output : Returns true on success, false on failure.
  6746. //-----------------------------------------------------------------------------
  6747. bool PhonemeEditor::Emphasis_IsValid( void )
  6748. {
  6749. if ( m_Tags.GetNumSamples() > 0 )
  6750. return true;
  6751. return false;
  6752. }
  6753. //-----------------------------------------------------------------------------
  6754. // Purpose:
  6755. //-----------------------------------------------------------------------------
  6756. void PhonemeEditor::Emphasis_SelectPoints( void )
  6757. {
  6758. if ( GetMode() != MODE_EMPHASIS )
  6759. return;
  6760. RECT rcWork, rcEmphasis;
  6761. GetWorkspaceRect( rcWork );
  6762. Emphasis_GetRect( rcWork, rcEmphasis );
  6763. RECT rcSelection;
  6764. rcSelection.left = m_nStartX < m_nLastX ? m_nStartX : m_nLastX;
  6765. rcSelection.right = m_nStartX < m_nLastX ? m_nLastX : m_nStartX;
  6766. rcSelection.top = m_nStartY < m_nLastY ? m_nStartY : m_nLastY;
  6767. rcSelection.bottom = m_nStartY < m_nLastY ? m_nLastY : m_nStartY;
  6768. rcSelection.top = max( rcSelection.top, rcEmphasis.top );
  6769. rcSelection.bottom = min( rcSelection.bottom, rcEmphasis.bottom );
  6770. int eh, ew;
  6771. eh = rcEmphasis.bottom - rcEmphasis.top;
  6772. ew = rcEmphasis.right - rcEmphasis.left;
  6773. InflateRect( &rcSelection, 5, 5 );
  6774. if ( !w2() || !h2() )
  6775. return;
  6776. float fleft = GetTimeForPixel( rcSelection.left );
  6777. float fright = GetTimeForPixel( rcSelection.right );
  6778. float ftop = (float)( rcSelection.top - rcEmphasis.top ) / (float)eh;
  6779. float fbottom = (float)( rcSelection.bottom- rcEmphasis.top ) / (float)eh;
  6780. //fleft = clamp( fleft, 0.0f, 1.0f );
  6781. //fright = clamp( fright, 0.0f, 1.0f );
  6782. ftop = clamp( ftop, 0.0f, 1.0f );
  6783. fbottom = clamp( fbottom, 0.0f, 1.0f );
  6784. float eps = 0.005;
  6785. for ( int i = 0; i < m_Tags.GetNumSamples(); i++ )
  6786. {
  6787. CEmphasisSample *sample = m_Tags.GetSample( i );
  6788. if ( sample->time + eps < fleft )
  6789. continue;
  6790. if ( sample->time - eps > fright )
  6791. continue;
  6792. if ( (1.0f - sample->value ) + eps < ftop )
  6793. continue;
  6794. if ( (1.0f - sample->value ) - eps > fbottom )
  6795. continue;
  6796. sample->selected = true;
  6797. }
  6798. redraw();
  6799. }
  6800. //-----------------------------------------------------------------------------
  6801. // Purpose:
  6802. // Input : rcHandle -
  6803. //-----------------------------------------------------------------------------
  6804. void PhonemeEditor::GetScrubHandleRect( RECT& rcHandle, bool clipped )
  6805. {
  6806. float pixel = 0.0f;
  6807. if ( m_pWaveFile )
  6808. {
  6809. float currenttime = m_flScrub;
  6810. float starttime, endtime;
  6811. GetScreenStartAndEndTime( starttime, endtime );
  6812. float screenfrac = ( currenttime - starttime ) / ( endtime - starttime );
  6813. pixel = screenfrac * w2();
  6814. if ( clipped )
  6815. {
  6816. pixel = clamp( pixel, SCRUBBER_HANDLE_WIDTH/2, w2() - SCRUBBER_HANDLE_WIDTH/2 );
  6817. }
  6818. }
  6819. rcHandle.left = pixel-SCRUBBER_HANDLE_WIDTH/2;
  6820. rcHandle.right = pixel + SCRUBBER_HANDLE_WIDTH/2;
  6821. rcHandle.top = 2 + GetCaptionHeight() + 12;
  6822. rcHandle.bottom = rcHandle.top + SCRUBBER_HANDLE_HEIGHT;
  6823. }
  6824. //-----------------------------------------------------------------------------
  6825. // Purpose:
  6826. // Input : rcArea -
  6827. //-----------------------------------------------------------------------------
  6828. void PhonemeEditor::GetScrubAreaRect( RECT& rcArea )
  6829. {
  6830. rcArea.left = 0;
  6831. rcArea.right = w2();
  6832. rcArea.top = 2 + GetCaptionHeight() + 12;
  6833. rcArea.bottom = rcArea.top + SCRUBBER_HEIGHT - 4;
  6834. }
  6835. void PhonemeEditor::DrawScrubHandle()
  6836. {
  6837. RECT rcHandle;
  6838. GetScrubHandleRect( rcHandle, true );
  6839. rcHandle.left = 0;
  6840. rcHandle.right = w2();
  6841. CChoreoWidgetDrawHelper drawHelper( this, rcHandle );
  6842. DrawScrubHandle( drawHelper );
  6843. }
  6844. //-----------------------------------------------------------------------------
  6845. // Purpose:
  6846. // Input : drawHelper -
  6847. // rcHandle -
  6848. //-----------------------------------------------------------------------------
  6849. void PhonemeEditor::DrawScrubHandle( CChoreoWidgetDrawHelper& drawHelper )
  6850. {
  6851. RECT rcHandle;
  6852. GetScrubHandleRect( rcHandle, true );
  6853. HBRUSH br = CreateSolidBrush( RGB( 0, 150, 100 ) );
  6854. COLORREF areaBorder = RGB( 230, 230, 220 );
  6855. drawHelper.DrawColoredLine( areaBorder,
  6856. PS_SOLID, 1, 0, rcHandle.top, w2(), rcHandle.top );
  6857. drawHelper.DrawColoredLine( areaBorder,
  6858. PS_SOLID, 1, 0, rcHandle.bottom, w2(), rcHandle.bottom );
  6859. drawHelper.DrawFilledRect( br, rcHandle );
  6860. //
  6861. char sz[ 32 ];
  6862. sprintf( sz, "%.3f", m_flScrub );
  6863. int len = drawHelper.CalcTextWidth( "Arial", 9, 500, sz );
  6864. RECT rcText = rcHandle;
  6865. int textw = rcText.right - rcText.left;
  6866. rcText.left += ( textw - len ) / 2;
  6867. drawHelper.DrawColoredText( "Arial", 9, 500, RGB( 255, 255, 255 ), rcText, sz );
  6868. DeleteObject( br );
  6869. }
  6870. //-----------------------------------------------------------------------------
  6871. // Purpose:
  6872. // Input : *event -
  6873. // Output : Returns true on success, false on failure.
  6874. //-----------------------------------------------------------------------------
  6875. bool PhonemeEditor::IsMouseOverScrubHandle( mxEvent *event )
  6876. {
  6877. RECT rcHandle;
  6878. GetScrubHandleRect( rcHandle, true );
  6879. InflateRect( &rcHandle, 2, 2 );
  6880. POINT pt;
  6881. pt.x = (short)event->x;
  6882. pt.y = (short)event->y;
  6883. if ( PtInRect( &rcHandle, pt ) )
  6884. {
  6885. return true;
  6886. }
  6887. return false;
  6888. }
  6889. bool PhonemeEditor::IsMouseOverScrubArea( mxEvent *event )
  6890. {
  6891. RECT rcArea;
  6892. rcArea.left = 0;
  6893. rcArea.right = w2();
  6894. rcArea.top = 2 + GetCaptionHeight() + 12;
  6895. rcArea.bottom = rcArea.top + 10;
  6896. InflateRect( &rcArea, 2, 2 );
  6897. POINT pt;
  6898. pt.x = (short)event->x;
  6899. pt.y = (short)event->y;
  6900. if ( PtInRect( &rcArea, pt ) )
  6901. {
  6902. return true;
  6903. }
  6904. return false;
  6905. }
  6906. float PhonemeEditor::GetTimeForSample( int sample )
  6907. {
  6908. if ( !m_pWaveFile )
  6909. {
  6910. return 0.0f;
  6911. }
  6912. float duration = m_pWaveFile->GetRunningLength();
  6913. int sampleCount = m_pWaveFile->SampleCount();
  6914. if ( sampleCount <= 0 )
  6915. return 0.0f;
  6916. float frac = (float)sample / (float)sampleCount;
  6917. return frac * duration;
  6918. }
  6919. void PhonemeEditor::ClampTimeToSelectionInterval( float& timeval )
  6920. {
  6921. if ( !m_pWaveFile )
  6922. {
  6923. return;
  6924. }
  6925. if ( !m_pMixer || !sound->IsSoundPlaying( m_pMixer ) )
  6926. {
  6927. return;
  6928. }
  6929. if ( !m_bSelectionActive )
  6930. return;
  6931. float starttime = GetTimeForSample( m_nSelection[ 0 ] );
  6932. float endtime = GetTimeForSample( m_nSelection[ 1 ] );
  6933. Assert( starttime <= endtime );
  6934. timeval = clamp( timeval, starttime, endtime );
  6935. }
  6936. void PhonemeEditor::ScrubThink( float dt, bool scrubbing )
  6937. {
  6938. ClampTimeToSelectionInterval( m_flScrub );
  6939. ClampTimeToSelectionInterval( m_flScrubTarget );
  6940. if ( m_flScrubTarget == m_flScrub && !scrubbing )
  6941. {
  6942. if ( sound->IsSoundPlaying( m_pMixer ) )
  6943. {
  6944. sound->StopSound( m_pMixer );
  6945. }
  6946. return;
  6947. }
  6948. if ( !m_pWaveFile )
  6949. return;
  6950. bool newmixer = false;
  6951. if ( !m_pMixer || !sound->IsSoundPlaying( m_pMixer ) )
  6952. {
  6953. m_pMixer = NULL;
  6954. SaveLinguisticData();
  6955. StudioModel *model = NULL;//models->GetActiveStudioModel();
  6956. sound->PlaySound( model, VOL_NORM, m_WorkFile.m_szWorkingFile, &m_pMixer );
  6957. newmixer = true;
  6958. }
  6959. if ( !m_pMixer )
  6960. {
  6961. return;
  6962. }
  6963. if ( m_flScrub > m_flScrubTarget )
  6964. {
  6965. m_pMixer->SetDirection( false );
  6966. }
  6967. else
  6968. {
  6969. m_pMixer->SetDirection( true );
  6970. }
  6971. float duration = m_pWaveFile->GetRunningLength();
  6972. if ( !duration )
  6973. return;
  6974. float d = m_flScrubTarget - m_flScrub;
  6975. int sign = d > 0.0f ? 1 : -1;
  6976. float maxmove = dt * m_flPlaybackRate;
  6977. if ( sign > 0 )
  6978. {
  6979. if ( d < maxmove )
  6980. {
  6981. m_flScrub = m_flScrubTarget;
  6982. }
  6983. else
  6984. {
  6985. m_flScrub += maxmove;
  6986. }
  6987. }
  6988. else
  6989. {
  6990. if ( -d < maxmove )
  6991. {
  6992. m_flScrub = m_flScrubTarget;
  6993. }
  6994. else
  6995. {
  6996. m_flScrub -= maxmove;
  6997. }
  6998. }
  6999. int sampleCount = m_pMixer->GetSource()->SampleCount();
  7000. int cursample = sampleCount * ( m_flScrub / duration );
  7001. int realsample = m_pMixer->GetSamplePosition();
  7002. int dsample = cursample - realsample;
  7003. int onehundredth = m_pMixer->GetSource()->SampleRate() * 0.01f;
  7004. if ( abs( dsample ) > onehundredth )
  7005. {
  7006. m_pMixer->SetSamplePosition( cursample, true );
  7007. }
  7008. m_pMixer->SetActive( true );
  7009. RECT rcArea;
  7010. GetScrubAreaRect( rcArea );
  7011. CChoreoWidgetDrawHelper drawHelper( this, rcArea );
  7012. DrawScrubHandle( drawHelper );
  7013. if ( scrubbing )
  7014. {
  7015. g_pMatSysWindow->Frame();
  7016. }
  7017. }
  7018. void PhonemeEditor::SetScrubTime( float t )
  7019. {
  7020. m_flScrub = t;
  7021. ClampTimeToSelectionInterval( m_flScrub );
  7022. }
  7023. void PhonemeEditor::SetScrubTargetTime( float t )
  7024. {
  7025. m_flScrubTarget = t;
  7026. ClampTimeToSelectionInterval( m_flScrubTarget );
  7027. }
  7028. void PhonemeEditor::OnToggleVoiceDuck()
  7029. {
  7030. SetDirty( true );
  7031. PushUndo();
  7032. m_Tags.SetVoiceDuck( !m_Tags.GetVoiceDuck() );
  7033. PushRedo();
  7034. redraw();
  7035. }
  7036. void PhonemeEditor::Play()
  7037. {
  7038. PlayEditedWave( m_bSelectionActive );
  7039. }