Source code of Windows XP (NT5)
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.

2479 lines
69 KiB

  1. //////////////////////////////////////////////////////////////////////
  2. // PromptDb.cpp : Implementation of CPromptDb
  3. //
  4. // Created by JOEM 03-2000
  5. // Copyright (C) 2000 Microsoft Corporation
  6. // All Rights Reserved
  7. //
  8. /////////////////////////////////////////////////////// JOEM 3-2000 //
  9. #include "stdafx.h"
  10. #include "PromptDb.h"
  11. #include "LocalTTSEngineSite.h"
  12. #include "vapiIO.h"
  13. #include <LIMITS>
  14. //#define ENTRY_TAG L"DB_ENTRY"
  15. const WCHAR ENTRY_TAG[] = L"DB_ENTRY";
  16. const WCHAR ENTRY_START[] = L"{";
  17. const WCHAR ENTRY_END[] = L"}";
  18. const WCHAR ID_TAG[] = L"ID:";
  19. const WCHAR TEXT_TAG[] = L"TEXT:";
  20. const WCHAR ORIGINAL_TAG[] = L"ORIGINAL:";
  21. const WCHAR TAG_TAG[] = L"TAG:";
  22. const WCHAR FILE_TAG[] = L"FILE:";
  23. const WCHAR FROM_TAG[] = L"FROM:";
  24. const WCHAR TO_TAG[] = L"TO:";
  25. const WCHAR START_PHONE_TAG[] = L"START_PHONE:";
  26. const WCHAR END_PHONE_TAG[] = L"END_PHONE:";
  27. const WCHAR RIGHT_CONTEXT_TAG[] = L"RIGHT_CONTEXT:";
  28. const WCHAR LEFT_CONTEXT_TAG[] = L"LEFT_CONTEXT:";
  29. const int g_iBase = 16;
  30. //////////////////////////////////////////////////////////////////////
  31. // Db
  32. //
  33. // Construction/Destruction
  34. /////////////////////////////////////////////////////// JOEM 3-2000 //
  35. CDb::CDb()
  36. {
  37. pszPathName = NULL;
  38. pszTempName = NULL;
  39. pszLogicalName = NULL;
  40. }
  41. CDb::~CDb()
  42. {
  43. if ( pszPathName )
  44. {
  45. free ( pszPathName );
  46. pszPathName = NULL;
  47. }
  48. if ( pszTempName )
  49. {
  50. free ( pszTempName );
  51. pszTempName = NULL;
  52. }
  53. if ( pszLogicalName )
  54. {
  55. free ( pszLogicalName );
  56. pszLogicalName = NULL;
  57. }
  58. }
  59. //////////////////////////////////////////////////////////////////////
  60. // CPromptDb
  61. //
  62. // Construction/Destruction
  63. /////////////////////////////////////////////////////// JOEM 3-2000 //
  64. HRESULT CPromptDb::FinalConstruct()
  65. {
  66. SPDBG_FUNC( "CPromptDb::FinalConstruct" );
  67. HRESULT hr = S_OK;
  68. m_pActiveDb = NULL;
  69. m_unIndex = 0;
  70. m_vcRef = 1;
  71. m_pOutputFormatId = NULL;
  72. m_pOutputFormat = NULL;
  73. m_flEntryGain = 1.0; // This will be set in registry.
  74. m_flXMLVolume = 1.0; // Full volume unless XML changes it.
  75. m_flXMLRateAdj = 1.0; // Regular rate unless XML changes it.
  76. if ( SUCCEEDED( hr ) )
  77. {
  78. m_pVapiIO = VapiIO::ClassFactory();
  79. if ( !m_pVapiIO )
  80. {
  81. hr = E_OUTOFMEMORY;
  82. }
  83. }
  84. // create the rate changer
  85. if ( SUCCEEDED(hr) )
  86. {
  87. m_pTsm = new CTsm( g_dRateScale[BASE_RATE] );
  88. if ( !m_pTsm )
  89. {
  90. hr = E_OUTOFMEMORY;
  91. }
  92. }
  93. SPDBG_REPORT_ON_FAIL( hr );
  94. return hr;
  95. }
  96. void CPromptDb::FinalRelease()
  97. {
  98. USHORT i = 0;
  99. m_pActiveDb = NULL;
  100. for ( i=0; i < m_apDbList.GetSize(); i++ )
  101. {
  102. if ( m_apDbList[i] )
  103. {
  104. delete m_apDbList[i];
  105. m_apDbList[i] = NULL;
  106. }
  107. }
  108. m_apDbList.RemoveAll();
  109. for ( i=0; i<m_aSearchList.GetSize(); i++ )
  110. {
  111. m_aSearchList[i].dstr.Clear();
  112. }
  113. m_aSearchList.RemoveAll();
  114. if ( m_pVapiIO )
  115. {
  116. delete m_pVapiIO;
  117. m_pVapiIO = NULL;
  118. }
  119. if ( m_pTsm )
  120. {
  121. delete m_pTsm;
  122. m_pTsm = NULL;
  123. }
  124. if ( m_pOutputFormat )
  125. {
  126. free(m_pOutputFormat);
  127. m_pOutputFormat = NULL;
  128. m_pOutputFormatId = NULL;
  129. }
  130. }
  131. //
  132. // IUnknown implementations
  133. //
  134. //////////////////////////////////////////////////////////////////////
  135. // CPromptDb::QueryInterface
  136. //
  137. /////////////////////////////////////////////////////// JOEM 4-2000 //
  138. STDMETHODIMP CPromptDb::QueryInterface(const IID& iid, void** ppv)
  139. {
  140. if ( iid == IID_IUnknown )
  141. {
  142. *ppv = (IPromptDb*) this;
  143. }
  144. else if ( iid == IID_IPromptDb )
  145. {
  146. *ppv = (IPromptDb*) this;
  147. }
  148. else
  149. {
  150. *ppv = NULL;
  151. return E_NOINTERFACE;
  152. }
  153. ((IUnknown*) *ppv)->AddRef();
  154. return S_OK;
  155. }
  156. //////////////////////////////////////////////////////////////////////
  157. // CPromptDb::AddRef
  158. //
  159. /////////////////////////////////////////////////////// JOEM 4-2000 //
  160. ULONG CPromptDb::AddRef()
  161. {
  162. return InterlockedIncrement(&m_vcRef);
  163. }
  164. //////////////////////////////////////////////////////////////////////
  165. // CPromptDb::Release
  166. //
  167. /////////////////////////////////////////////////////// JOEM 4-2000 //
  168. ULONG CPromptDb::Release()
  169. {
  170. if ( 0 == InterlockedDecrement(&m_vcRef) )
  171. {
  172. delete this;
  173. return 0;
  174. }
  175. return (ULONG) m_vcRef;
  176. }
  177. //
  178. // IPromptDb INTERFACE FUNCTION IMPLEMENTATIONS
  179. //
  180. //////////////////////////////////////////////////////////////////////
  181. // CPromptDb::NewDb
  182. //
  183. // Creates a new Db.
  184. //
  185. /////////////////////////////////////////////////////// JOEM 3-2000 //
  186. STDMETHODIMP CPromptDb::NewDb(const WCHAR* logicalName, const WCHAR* pathName)
  187. {
  188. SPDBG_FUNC( "CPromptDb::NewDb" );
  189. HRESULT hr = S_OK;
  190. CDb* db = NULL;
  191. // See if this Db already exists.
  192. hr = ActivateDbName(logicalName);
  193. if ( SUCCEEDED(hr) )
  194. {
  195. return E_INVALIDARG;
  196. }
  197. db = new CDb;
  198. if ( db )
  199. {
  200. hr = S_OK;
  201. }
  202. if ( SUCCEEDED(hr) )
  203. {
  204. db->pszLogicalName = _wcsdup(logicalName);
  205. if ( !db->pszLogicalName )
  206. {
  207. hr = E_OUTOFMEMORY;
  208. }
  209. }
  210. if ( SUCCEEDED(hr) )
  211. {
  212. db->pszPathName = _wcsdup(pathName);
  213. if ( !db->pszPathName )
  214. {
  215. hr = E_OUTOFMEMORY;
  216. }
  217. }
  218. if ( SUCCEEDED(hr) )
  219. {
  220. m_apDbList.Add(db);
  221. }
  222. if ( FAILED(hr) )
  223. {
  224. if ( db )
  225. {
  226. delete db;
  227. }
  228. }
  229. SPDBG_REPORT_ON_FAIL( hr );
  230. return hr;
  231. }
  232. //////////////////////////////////////////////////////////////////////
  233. // CPromptDb::AddDb
  234. //
  235. // Adds a Db to the list, and activates it if desired.
  236. //
  237. /////////////////////////////////////////////////////// JOEM 3-2000 //
  238. STDMETHODIMP CPromptDb::AddDb(const WCHAR* logicalName, const WCHAR* pathName, const WCHAR* pszIdSet, const USHORT loadOption)
  239. {
  240. SPDBG_FUNC( "CPromptDb::AddDb" );
  241. HRESULT hr = S_OK;
  242. CDb* pActiveDb = m_pActiveDb;
  243. if ( !pathName || !wcslen(pathName) )
  244. {
  245. hr = E_INVALIDARG;
  246. }
  247. if ( SUCCEEDED(hr) )
  248. {
  249. if ( !FileExist( pathName ) )
  250. {
  251. hr = PEERR_DB_NOT_FOUND;
  252. }
  253. }
  254. if ( SUCCEEDED(hr) )
  255. {
  256. // if there is logicalName specified, activate or create it.
  257. if ( logicalName )
  258. {
  259. hr = ActivateDbName(logicalName);
  260. if ( FAILED(hr) )
  261. {
  262. hr = NewDb(logicalName, pathName);
  263. if ( SUCCEEDED(hr) )
  264. {
  265. hr = ActivateDbName(logicalName);
  266. }
  267. }
  268. }
  269. else
  270. {
  271. // if no current Db, activate the default
  272. if ( !m_pActiveDb )
  273. {
  274. hr = ActivateDbName(L"DEFAULT");
  275. }
  276. }
  277. }
  278. // Set the Active Db's new path name
  279. if ( SUCCEEDED(hr) )
  280. {
  281. if ( m_pActiveDb->pszPathName )
  282. {
  283. free(m_pActiveDb->pszPathName);
  284. m_pActiveDb->pszPathName = wcsdup(pathName);
  285. if ( !m_pActiveDb->pszPathName )
  286. {
  287. hr = E_OUTOFMEMORY;
  288. }
  289. }
  290. }
  291. // Load it?
  292. if ( SUCCEEDED(hr) )
  293. {
  294. if ( loadOption == DB_LOAD )
  295. {
  296. if ( SUCCEEDED( hr ) )
  297. {
  298. hr = LoadDb(pszIdSet);
  299. }
  300. }
  301. }
  302. // Reset the active Db
  303. m_pActiveDb = pActiveDb;
  304. SPDBG_REPORT_ON_FAIL( hr );
  305. return hr;
  306. }
  307. //////////////////////////////////////////////////////////////////////
  308. // CPromptDb::LoadDb
  309. //
  310. // Initializes the hash tables for a new Db.
  311. //
  312. /////////////////////////////////////////////////////// JOEM 3-2000 //
  313. STDMETHODIMP CPromptDb::LoadDb(const WCHAR* pszIdSet)
  314. {
  315. SPDBG_FUNC( "CPromptDb::LoadDb" );
  316. HRESULT hr = S_OK;
  317. FILE* fp = NULL;
  318. SPDBG_ASSERT( m_pActiveDb );
  319. SPDBG_ASSERT( m_pActiveDb->pszPathName );
  320. fp = _wfopen (m_pActiveDb->pszPathName, L"r");
  321. if ( !fp )
  322. {
  323. hr = E_ACCESSDENIED;
  324. }
  325. if ( SUCCEEDED(hr) )
  326. {
  327. hr = LoadIdHash(fp, pszIdSet);
  328. fclose (fp);
  329. }
  330. if ( SUCCEEDED(hr) )
  331. {
  332. hr = IndexTextHash();
  333. }
  334. SPDBG_REPORT_ON_FAIL( hr );
  335. return hr;
  336. }
  337. //////////////////////////////////////////////////////////////////////
  338. // CPromptDb::UnloadDb
  339. //
  340. // Deactivates and unloads a Db. Makes the default Db active, or the
  341. // first available Db if the default doesn't exist.
  342. //
  343. /////////////////////////////////////////////////////// JOEM 3-2000 //
  344. STDMETHODIMP CPromptDb::UnloadDb(const WCHAR* pszLogicalName)
  345. {
  346. SPDBG_FUNC( "CPromptDb::UnloadDb" );
  347. HRESULT hr = S_OK;
  348. USHORT i = 0;
  349. USHORT dbIndex = 0;
  350. SPDBG_ASSERT(pszLogicalName);
  351. for ( i=0; i<m_apDbList.GetSize(); i++ )
  352. {
  353. if ( wcscmp(pszLogicalName, m_apDbList[i]->pszLogicalName) == 0 )
  354. {
  355. if ( wcscmp(pszLogicalName, m_pActiveDb->pszLogicalName) == 0 )
  356. {
  357. m_pActiveDb = NULL;
  358. }
  359. delete m_apDbList[i];
  360. m_apDbList[i] = NULL;
  361. m_apDbList.RemoveAt( i );
  362. }
  363. }
  364. if ( !m_pActiveDb )
  365. {
  366. hr = ActivateDbName(L"DEFAULT");
  367. if ( FAILED(hr) )
  368. {
  369. hr = ActivateDbNumber(0);
  370. }
  371. if( FAILED(hr) )
  372. {
  373. hr = S_FALSE;
  374. }
  375. }
  376. SPDBG_REPORT_ON_FAIL( hr );
  377. return hr;
  378. }
  379. //////////////////////////////////////////////////////////////////////
  380. // CPromptDb::ActivateDbName
  381. //
  382. // Activates the Db specified by logical name.
  383. //
  384. /////////////////////////////////////////////////////// JOEM 3-2000 //
  385. STDMETHODIMP CPromptDb::ActivateDbName(const WCHAR* pszLogicalName)
  386. {
  387. SPDBG_FUNC( "CPromptDb::ActivateDbName" );
  388. HRESULT hr = PEERR_DB_NOT_FOUND;
  389. WCHAR* pszDb = NULL;
  390. USHORT unSize = 0;
  391. USHORT i = 0;
  392. SPDBG_ASSERT(pszLogicalName);
  393. if ( m_pActiveDb )
  394. {
  395. // See if the Db is already the active one
  396. if ( !wcscmp(m_pActiveDb->pszLogicalName, pszLogicalName ) )
  397. {
  398. hr = S_OK;
  399. }
  400. }
  401. // Did the current Db match?
  402. if ( FAILED(hr) )
  403. {
  404. unSize = (USHORT) m_apDbList.GetSize();
  405. for (i = 0; i < unSize; i++)
  406. {
  407. if ( _wcsicmp(pszLogicalName, m_apDbList[i]->pszLogicalName) == 0 )
  408. {
  409. m_pActiveDb = m_apDbList[i];
  410. hr = S_OK;
  411. }
  412. }
  413. }
  414. // Don't report failures here - this func is allowed to fail frequently.
  415. //SPDBG_REPORT_ON_FAIL( hr );
  416. return hr;
  417. }
  418. //////////////////////////////////////////////////////////////////////
  419. // CPromptDb::ActivateDbNumber
  420. //
  421. // Activates the Db specified by number.
  422. //
  423. /////////////////////////////////////////////////////// JOEM 3-2000 //
  424. STDMETHODIMP CPromptDb::ActivateDbNumber(const USHORT unIndex)
  425. {
  426. SPDBG_FUNC( "CPromptDb::ActivateDbNumber" );
  427. HRESULT hr = S_OK;
  428. USHORT unSize = 0;
  429. USHORT i = 0;
  430. unSize = (USHORT) m_apDbList.GetSize();
  431. if ( unIndex >= unSize )
  432. {
  433. hr = PEERR_DB_NOT_FOUND;
  434. }
  435. if ( SUCCEEDED(hr) )
  436. {
  437. m_pActiveDb = m_apDbList[unIndex];
  438. if ( !m_pActiveDb )
  439. {
  440. hr = E_UNEXPECTED;
  441. }
  442. }
  443. SPDBG_REPORT_ON_FAIL( hr );
  444. return hr;
  445. }
  446. //////////////////////////////////////////////////////////////////////
  447. // CPromptDb::UpdateDb
  448. //
  449. // When creating a Db file, this updates the real Db file with the
  450. // contents of the temp file.
  451. //
  452. /////////////////////////////////////////////////////// JOEM 3-2000 //
  453. STDMETHODIMP CPromptDb::UpdateDb(WCHAR* pszPath)
  454. {
  455. SPDBG_FUNC( "CPromptDb::UpdateDb" );
  456. HRESULT hr = S_OK;
  457. FILE* fp = NULL;
  458. WCHAR* id = NULL;
  459. CPromptEntry* value = NULL;
  460. USHORT idx1 = 0;
  461. USHORT idx2 = 0;
  462. WCHAR backupName[_MAX_PATH + 1];
  463. SPDBG_ASSERT( m_pActiveDb );
  464. // New name?
  465. if ( pszPath )
  466. {
  467. if ( m_pActiveDb->pszPathName )
  468. {
  469. free( m_pActiveDb->pszPathName);
  470. }
  471. m_pActiveDb->pszPathName = wcsdup(pszPath);
  472. if ( !m_pActiveDb->pszPathName )
  473. {
  474. hr = E_OUTOFMEMORY;
  475. }
  476. }
  477. // Create the temp file name
  478. if ( SUCCEEDED(hr) )
  479. {
  480. hr = TempName();
  481. }
  482. // Write the entries to the temp file
  483. if ( SUCCEEDED(hr) )
  484. {
  485. if ((fp = _wfopen(m_pActiveDb->pszTempName, L"w")) == NULL)
  486. {
  487. hr = E_ACCESSDENIED;
  488. }
  489. if ( SUCCEEDED(hr) )
  490. {
  491. fwprintf (fp, L"#\n# PROMPT Database, generated automatically\n#\n\n");
  492. while ( SUCCEEDED(hr) )
  493. {
  494. hr = m_pActiveDb->idHash.NextKey(&idx1, &idx2, &id);
  495. if ( id == NULL )
  496. {
  497. break;
  498. }
  499. if ( SUCCEEDED(hr) )
  500. {
  501. value = (CPromptEntry *) m_pActiveDb->idHash.Find(id);
  502. if ( !value )
  503. {
  504. hr = E_FAIL;
  505. }
  506. }
  507. if ( SUCCEEDED(hr) )
  508. {
  509. hr = WriteEntry (fp, value);
  510. }
  511. }
  512. fclose (fp);
  513. }
  514. }
  515. // Now backup the old Db file
  516. if ( SUCCEEDED(hr) )
  517. {
  518. // Make a backupName
  519. wcscat( wcsncpy( backupName, m_pActiveDb->pszPathName, _MAX_PATH-1 ), L"~" );
  520. // This "remove" is necessary, since the "rename"
  521. // would fail if the file exists.
  522. if ( FileExist( backupName ) )
  523. {
  524. _wremove (backupName);
  525. }
  526. if ( FileExist( m_pActiveDb->pszPathName) )
  527. {
  528. if ( _wrename( m_pActiveDb->pszPathName, backupName ) !=0 )
  529. {
  530. hr = E_ACCESSDENIED;
  531. }
  532. }
  533. }
  534. if ( SUCCEEDED(hr) )
  535. {
  536. if ( _wrename( m_pActiveDb->pszTempName, m_pActiveDb->pszPathName ) !=0 )
  537. {
  538. hr = E_ACCESSDENIED;
  539. }
  540. }
  541. SPDBG_REPORT_ON_FAIL( hr );
  542. return hr;
  543. }
  544. //////////////////////////////////////////////////////////////////////
  545. // CPromptDb
  546. //
  547. // CountDb
  548. //
  549. /////////////////////////////////////////////////////// JOEM 3-2000 //
  550. STDMETHODIMP CPromptDb::CountDb(USHORT *unCount)
  551. {
  552. SPDBG_FUNC( "CPromptDb::CountDb" );
  553. *unCount = (USHORT) m_apDbList.GetSize();
  554. return S_OK;
  555. }
  556. //////////////////////////////////////////////////////////////////////
  557. // CPromptDb::LoadIdHash
  558. //
  559. // Makes a hash table for entry ID numbers, using unique key values.
  560. // Adds all the Db entry ids to the table.
  561. //
  562. /////////////////////////////////////////////////////// JOEM 3-2000 //
  563. HRESULT CPromptDb::LoadIdHash(FILE *fp, const WCHAR* pszIdSet)
  564. {
  565. SPDBG_FUNC( "CPromptDb::LoadIdHash" );
  566. HRESULT hr = S_OK;
  567. CPromptEntry* entry = NULL;
  568. const WCHAR* entryInfo = NULL;
  569. SPDBG_ASSERT (fp);
  570. // S_FALSE indicates no more entries.
  571. while ( hr == S_OK )
  572. {
  573. hr = ReadEntry (fp, &entry);
  574. if ( hr == S_OK && entry )
  575. {
  576. if ( SUCCEEDED(hr) )
  577. {
  578. hr = entry->GetId(&entryInfo);
  579. // prepend the IDSET info to the id
  580. if ( pszIdSet && wcslen(pszIdSet) )
  581. {
  582. WCHAR* pszFullId = NULL;
  583. int iLen = wcslen(pszIdSet) + wcslen(entryInfo) + 1;
  584. pszFullId = new WCHAR[iLen];
  585. if ( pszFullId )
  586. {
  587. wcscpy(pszFullId, pszIdSet);
  588. wcscat(pszFullId, entryInfo);
  589. hr = entry->SetId(pszFullId);
  590. if ( SUCCEEDED(hr) )
  591. {
  592. hr = entry->GetId(&entryInfo);
  593. }
  594. delete [] pszFullId;
  595. }
  596. else
  597. {
  598. hr = E_OUTOFMEMORY;
  599. }
  600. }
  601. if ( SUCCEEDED(hr) )
  602. {
  603. hr = m_pActiveDb->idHash.BuildEntry( entryInfo, entry );
  604. if ( hr == E_INVALIDARG )
  605. {
  606. hr = PEERR_DB_DUPLICATE_ID;
  607. }
  608. entryInfo = NULL;
  609. }
  610. }
  611. entry->Release();
  612. }
  613. }
  614. SPDBG_REPORT_ON_FAIL( hr );
  615. return hr;
  616. }
  617. //////////////////////////////////////////////////////////////////////
  618. // CPromptDb::SearchDb
  619. //
  620. // Searches the text in the Db entries for items that can be
  621. // concatenated to match the query text. If such a list exists,
  622. // it is put in m_aSearchList.
  623. //
  624. /////////////////////////////////////////////////////// JOEM 3-2000 //
  625. STDMETHODIMP CPromptDb::SearchDb(const WCHAR* pszQuery, USHORT* unIdCount)
  626. {
  627. SPDBG_FUNC( "CPromptDb::SearchDb" );
  628. HRESULT hr = S_OK;
  629. const CDynStrArray* list = NULL;
  630. USHORT i = 0;
  631. IPromptEntry* pIPE = NULL;
  632. // make sure the search list is clear
  633. for ( i=0; i<m_aSearchList.GetSize(); i++ )
  634. {
  635. m_aSearchList[i].dstr.Clear();
  636. }
  637. m_aSearchList.RemoveAll();
  638. *unIdCount = 0;
  639. if(m_pActiveDb)
  640. {
  641. list = (CDynStrArray*) m_pActiveDb->textHash.Find(pszQuery);
  642. }
  643. if ( !list )
  644. {
  645. hr = E_FAIL;
  646. }
  647. else
  648. {
  649. // If searching the text hash resulted in a list of Ids, save the list.
  650. for ( i=0; i<list->m_aDstr.GetSize(); i++ )
  651. {
  652. m_aSearchList.Add( list->m_aDstr[i].dstr );
  653. }
  654. }
  655. if ( SUCCEEDED(hr) )
  656. {
  657. // go through the list of IDs for this text
  658. // Make sure each id in the list exists; if not, remove it from list.
  659. for ( i=0; i<m_aSearchList.GetSize(); i++)
  660. {
  661. hr = E_FAIL;
  662. // if FindEntry fails, the list size changes and list#'s shift down, so keep checking i
  663. while ( FAILED(hr) && i < m_aSearchList.GetSize() )
  664. {
  665. hr = FindEntry( m_aSearchList[i].dstr, &pIPE );
  666. if ( SUCCEEDED(hr) )
  667. {
  668. pIPE->Release();
  669. }
  670. else
  671. {
  672. m_aSearchList[i].dstr.Clear();
  673. m_aSearchList.RemoveAt( i );
  674. }
  675. }
  676. }
  677. }
  678. if ( m_aSearchList.GetSize() )
  679. {
  680. *unIdCount = (USHORT) m_aSearchList.GetSize();
  681. hr = S_OK;
  682. }
  683. // Don't report failures here - this func is allowed to fail frequently.
  684. //SPDBG_REPORT_ON_FAIL( hr );
  685. return hr;
  686. }
  687. //////////////////////////////////////////////////////////////////////
  688. // CPromptDb::RetrieveSearchItem
  689. //
  690. //
  691. /////////////////////////////////////////////////////// JOEM 5-2000 //
  692. STDMETHODIMP CPromptDb::RetrieveSearchItem(const USHORT unId, const WCHAR** ppszId)
  693. {
  694. SPDBG_FUNC( "CPromptDb::RetrieveSearchItem" );
  695. HRESULT hr = S_OK;
  696. if ( unId >= m_aSearchList.GetSize() )
  697. {
  698. hr = E_INVALIDARG;
  699. }
  700. if ( SUCCEEDED(hr) )
  701. {
  702. *ppszId = m_aSearchList[unId].dstr;
  703. }
  704. SPDBG_REPORT_ON_FAIL( hr );
  705. return hr;
  706. }
  707. //////////////////////////////////////////////////////////////////////
  708. // CPromptDb::GetLogicalName
  709. //
  710. //
  711. /////////////////////////////////////////////////////// JOEM 3-2000 //
  712. STDMETHODIMP CPromptDb::GetLogicalName(const WCHAR** ppszLogicalName)
  713. {
  714. SPDBG_FUNC( "CPromptDb::GetLogicalName" );
  715. HRESULT hr = S_OK;
  716. SPDBG_ASSERT( m_pActiveDb );
  717. *ppszLogicalName = m_pActiveDb->pszLogicalName;
  718. SPDBG_REPORT_ON_FAIL( hr );
  719. return hr;
  720. }
  721. //////////////////////////////////////////////////////////////////////
  722. // CPromptDb::FindEntry
  723. //
  724. // Locates a Db entry in the hash, and gets an interface for it.
  725. //
  726. /////////////////////////////////////////////////////// JOEM 3-2000 //
  727. STDMETHODIMP CPromptDb::FindEntry(const WCHAR *id, IPromptEntry** ppIPE)
  728. {
  729. SPDBG_FUNC( "CPromptDb::FindEntry" );
  730. HRESULT hr = S_OK;
  731. CPromptEntry* entry = NULL;
  732. const WCHAR* entryFileName = NULL;
  733. SPDBG_ASSERT(m_pActiveDb);
  734. SPDBG_ASSERT(id);
  735. entry = (CPromptEntry*) m_pActiveDb->idHash.Find(id);
  736. if ( !entry )
  737. {
  738. hr = PEERR_DB_ID_NOT_FOUND;
  739. }
  740. if ( SUCCEEDED(hr) )
  741. {
  742. hr = entry->GetFileName(&entryFileName);
  743. if ( SUCCEEDED(hr) )
  744. {
  745. if ( FileExist( entryFileName ) )
  746. {
  747. hr = entry->QueryInterface(IID_IPromptEntry, (void**)ppIPE);
  748. }
  749. else
  750. {
  751. *ppIPE = NULL;
  752. hr = E_ACCESSDENIED;
  753. }
  754. entryFileName = NULL;
  755. }
  756. }
  757. SPDBG_REPORT_ON_FAIL( hr );
  758. return hr;
  759. }
  760. //////////////////////////////////////////////////////////////////////
  761. // CPromptDb::NewEntry
  762. //
  763. /////////////////////////////////////////////////////// JOEM 5-2000 //
  764. STDMETHODIMP CPromptDb::NewEntry(IPromptEntry **ppIPE)
  765. {
  766. SPDBG_FUNC( "CPromptDb::NewEntry" );
  767. CPromptEntry* newEntry = new CPromptEntry;
  768. HRESULT hr = S_OK;
  769. if ( newEntry )
  770. {
  771. *ppIPE = newEntry;
  772. }
  773. else
  774. {
  775. hr = E_OUTOFMEMORY;
  776. }
  777. SPDBG_REPORT_ON_FAIL( hr );
  778. return hr;
  779. }
  780. //////////////////////////////////////////////////////////////////////
  781. // CPromptDb::SaveEntry
  782. //
  783. // Adds a Db entry to the Db's hash table
  784. //
  785. /////////////////////////////////////////////////////// JOEM 3-2000 //
  786. STDMETHODIMP CPromptDb::SaveEntry(IPromptEntry* pIPE)
  787. {
  788. SPDBG_FUNC( "CPromptDb::SaveEntry" );
  789. HRESULT hr = S_OK;
  790. const WCHAR* entryId = NULL;
  791. double to = 0.0;
  792. double from = 0.0;
  793. if ( SUCCEEDED(hr) )
  794. {
  795. hr = pIPE->GetStart(&from);
  796. }
  797. if ( SUCCEEDED(hr) )
  798. {
  799. hr = pIPE->GetEnd(&to);
  800. }
  801. if ( SUCCEEDED(hr) )
  802. {
  803. USHORT validTime = (from==0.0 && to == -1.0) || (from>=0.0 && to>from);
  804. const WCHAR* entryFile = NULL;
  805. const WCHAR* entryText = NULL;
  806. pIPE->GetId(&entryId);
  807. pIPE->GetText(&entryText);
  808. pIPE->GetFileName(&entryFile);
  809. if ( !entryId || !entryText || !entryFile || !validTime )
  810. {
  811. hr = E_INVALIDARG;
  812. }
  813. }
  814. if ( SUCCEEDED(hr) )
  815. {
  816. hr = m_pActiveDb->idHash.BuildEntry( entryId, pIPE );
  817. }
  818. SPDBG_REPORT_ON_FAIL( hr );
  819. return hr;
  820. }
  821. //////////////////////////////////////////////////////////////////////
  822. // CPromptDb::RemoveEntry
  823. //
  824. //
  825. /////////////////////////////////////////////////////// JOEM 3-2000 //
  826. STDMETHODIMP CPromptDb::RemoveEntry(const WCHAR *id)
  827. {
  828. SPDBG_FUNC( "CPromptDb::RemoveEntry" );
  829. HRESULT hr = S_OK;
  830. CPromptEntry* pEntry = NULL;
  831. SPDBG_ASSERT(m_pActiveDb);
  832. SPDBG_ASSERT(id);
  833. if (SUCCEEDED(hr))
  834. {
  835. // CHash will Release it
  836. hr = m_pActiveDb->idHash.DeleteEntry(id);
  837. }
  838. SPDBG_REPORT_ON_FAIL( hr );
  839. return hr;
  840. }
  841. //////////////////////////////////////////////////////////////////////
  842. // CPromptDb::OpenEntryFile
  843. //
  844. // Opens the specified entry.
  845. //
  846. /////////////////////////////////////////////////////// JOEM 5-2000 //
  847. STDMETHODIMP CPromptDb::OpenEntryFile(IPromptEntry *pIPE, WAVEFORMATEX* pWaveFormatEx)
  848. {
  849. SPDBG_FUNC( "CPromptDb::OpenEntryFile" );
  850. HRESULT hr = S_OK;
  851. const WCHAR* entryFile = NULL;
  852. SPDBG_ASSERT (pIPE);
  853. if ( SUCCEEDED(hr) )
  854. {
  855. hr = pIPE->GetFileName(&entryFile);
  856. }
  857. if ( SUCCEEDED(hr) )
  858. {
  859. if ( m_pVapiIO->OpenFile(entryFile, VAPI_IO_READ) != 0 )
  860. {
  861. hr = E_ACCESSDENIED;
  862. }
  863. entryFile = NULL;
  864. }
  865. if ( SUCCEEDED(hr) )
  866. {
  867. if ( m_pVapiIO->Format(NULL, NULL, pWaveFormatEx) != 0 )
  868. {
  869. hr = E_FAIL;
  870. }
  871. }
  872. if ( FAILED(hr) )
  873. {
  874. m_pVapiIO->CloseFile();
  875. }
  876. SPDBG_REPORT_ON_FAIL( hr );
  877. return hr;
  878. }
  879. //////////////////////////////////////////////////////////////////////
  880. // CPromptDb::CloseEntryFile
  881. //
  882. // Retrieves the specified audio samples from the database entry.
  883. //
  884. /////////////////////////////////////////////////////// JOEM 5-2000 //
  885. STDMETHODIMP CPromptDb::CloseEntryFile()
  886. {
  887. m_pVapiIO->CloseFile();
  888. return S_OK;
  889. }
  890. //////////////////////////////////////////////////////////////////////
  891. // CPromptDb::GetNextEntry
  892. //
  893. // Retrieves the entry, based on the unique key for the Id Hash.
  894. // To retrieve the first entry, use punId1=0, punId2=0. The Id's
  895. // will be updated with the values that can be used to retrieve the
  896. // next entry.
  897. //
  898. /////////////////////////////////////////////////////// JOEM 6-2000 //
  899. STDMETHODIMP CPromptDb::GetNextEntry(USHORT* punId1, USHORT* punId2, IPromptEntry** ppIPE)
  900. {
  901. SPDBG_FUNC( "CPromptDb::GetNextEntry" );
  902. HRESULT hr = S_OK;
  903. WCHAR* pszNextKey = NULL;
  904. SPDBG_ASSERT(punId1);
  905. SPDBG_ASSERT(punId2);
  906. hr = m_pActiveDb->idHash.NextKey( punId1, punId2, &pszNextKey );
  907. if ( !pszNextKey )
  908. {
  909. hr = E_FAIL;
  910. }
  911. if ( SUCCEEDED(hr) )
  912. {
  913. hr = FindEntry(pszNextKey, ppIPE);
  914. }
  915. SPDBG_REPORT_ON_FAIL( hr );
  916. return hr;
  917. }
  918. //////////////////////////////////////////////////////////////////////
  919. // CPromptDb::SetOutputFormat
  920. //
  921. // Sets the format in which samples are returned to the calling app.
  922. //
  923. /////////////////////////////////////////////////////// JOEM 5-2000 //
  924. STDMETHODIMP CPromptDb::SetOutputFormat(const GUID *pOutputFormatId, const WAVEFORMATEX *pOutputFormat)
  925. {
  926. SPDBG_FUNC( "CPromptDb::SetOutputFormat" );
  927. HRESULT hr = S_OK;
  928. if ( pOutputFormatId && *pOutputFormatId == SPDFID_Text )
  929. {
  930. m_pOutputFormatId = pOutputFormatId;
  931. if ( m_pOutputFormat )
  932. {
  933. delete m_pOutputFormat;
  934. m_pOutputFormat = NULL;
  935. }
  936. }
  937. else if ( pOutputFormat )
  938. {
  939. // Do we need to change the format?
  940. if ( !m_pOutputFormat ||
  941. m_pOutputFormat->wFormatTag != pOutputFormat->wFormatTag ||
  942. m_pOutputFormat->nChannels != pOutputFormat->nChannels ||
  943. m_pOutputFormat->nSamplesPerSec != pOutputFormat->nSamplesPerSec ||
  944. m_pOutputFormat->nAvgBytesPerSec != pOutputFormat->nAvgBytesPerSec ||
  945. m_pOutputFormat->nBlockAlign != pOutputFormat->nBlockAlign ||
  946. m_pOutputFormat->wBitsPerSample != pOutputFormat->wBitsPerSample ||
  947. m_pOutputFormat->cbSize != pOutputFormat->cbSize
  948. )
  949. {
  950. // free the current waveformatex
  951. if ( m_pOutputFormat )
  952. {
  953. free(m_pOutputFormat);
  954. m_pOutputFormat = NULL;
  955. }
  956. // this needs to copy the output format, not just point to it,
  957. // because engine will pass in const pointer.
  958. m_pOutputFormat = (WAVEFORMATEX*) malloc( sizeof(WAVEFORMATEX) + pOutputFormat->cbSize );
  959. if ( !m_pOutputFormat )
  960. {
  961. hr = E_OUTOFMEMORY;
  962. }
  963. else
  964. {
  965. m_pOutputFormatId = pOutputFormatId;
  966. memcpy(m_pOutputFormat, pOutputFormat, sizeof(WAVEFORMATEX) + pOutputFormat->cbSize );
  967. }
  968. if ( SUCCEEDED(hr) )
  969. {
  970. m_converter.SetOutputFormat(m_pOutputFormat);
  971. }
  972. if ( SUCCEEDED(hr) && m_pTsm )
  973. {
  974. m_pTsm->SetSamplingFrequency(m_pOutputFormat->nSamplesPerSec);
  975. }
  976. }
  977. }
  978. else
  979. {
  980. hr = E_INVALIDARG;
  981. }
  982. return hr;
  983. }
  984. //////////////////////////////////////////////////////////////////////
  985. // CPromptDb::SetEntryGain
  986. //
  987. // Sets the gain factor (from registry) for Prompt entries.
  988. //
  989. /////////////////////////////////////////////////////// JOEM 6-2000 //
  990. STDMETHODIMP CPromptDb::SetEntryGain(const double dEntryGain)
  991. {
  992. SPDBG_FUNC( "CPromptDb::SetEntryGain" );
  993. m_flEntryGain = (float)dEntryGain;
  994. return S_OK;
  995. }
  996. //////////////////////////////////////////////////////////////////////
  997. // CPromptDb::SetXMLVolume
  998. //
  999. // Adjust the volume from XML tag.
  1000. //
  1001. /////////////////////////////////////////////////////// JOEM 6-2000 //
  1002. STDMETHODIMP CPromptDb::SetXMLVolume(const ULONG ulXMLVol)
  1003. {
  1004. SPDBG_FUNC( "CPromptDb::SetXMLVolume" );
  1005. SPDBG_ASSERT(ulXMLVol <=100);
  1006. m_flXMLVolume = ((double) ulXMLVol) / 100;
  1007. return S_OK;
  1008. }
  1009. //////////////////////////////////////////////////////////////////////
  1010. // CPromptDb::SetXMLRate
  1011. //
  1012. // Adjust the rate from XML tag.
  1013. //
  1014. /////////////////////////////////////////////////////// JOEM 11-2000 //
  1015. STDMETHODIMP CPromptDb::SetXMLRate(const long lXMLRate)
  1016. {
  1017. SPDBG_FUNC( "CPromptDb::SetXMLRate" );
  1018. ComputeRateAdj(lXMLRate, &m_flXMLRateAdj);
  1019. return S_OK;
  1020. }
  1021. //////////////////////////////////////////////////////////////////////
  1022. // CPromptDb::SendEntrySamples
  1023. //
  1024. // Retrieves the specified audio samples from the database entry,
  1025. // applies rate change, converts them, applies gain/vol, and sends
  1026. // them to output site.
  1027. //
  1028. /////////////////////////////////////////////////////// JOEM 5-2000 //
  1029. STDMETHODIMP CPromptDb::SendEntrySamples(IPromptEntry* pIPE, ISpTTSEngineSite* pOutputSite, ULONG ulTextOffset, ULONG ulTextLen)
  1030. {
  1031. HRESULT hr = S_OK;
  1032. double entryTo = 0.0;
  1033. double entryFrom = 0.0;
  1034. ULONG pcbWritten = 0;
  1035. void* pvSamples = NULL;
  1036. void* pvNewSamples = NULL;
  1037. int iNumSamples = 0;
  1038. int iNumNewSamples = 0;
  1039. USHORT unVol = 1; // Full vol unless app changes it
  1040. double dVol = 1.0; // Full vol unless app changes it
  1041. long lRateAdj = 1; // Regular rate unless app changes it
  1042. float flRateAdj = 1.0; // Regular rate unless app changes it
  1043. double dBuff = 0.0;
  1044. USHORT i = 0;
  1045. bool fConvert = false;
  1046. WAVEFORMATEX wavFormat;
  1047. SPDBG_ASSERT(pIPE);
  1048. SPDBG_ASSERT(pOutputSite);
  1049. hr = OpenEntryFile(pIPE, &wavFormat);
  1050. if ( SUCCEEDED(hr) )
  1051. {
  1052. // Does the current prompt format need converting?
  1053. if ( m_pOutputFormat )
  1054. {
  1055. if (wavFormat.wFormatTag != m_pOutputFormat->wFormatTag ||
  1056. wavFormat.nAvgBytesPerSec != m_pOutputFormat->nAvgBytesPerSec ||
  1057. wavFormat.nBlockAlign != m_pOutputFormat->nBlockAlign ||
  1058. wavFormat.nChannels != m_pOutputFormat->nChannels ||
  1059. wavFormat.nSamplesPerSec != m_pOutputFormat->nSamplesPerSec ||
  1060. wavFormat.wBitsPerSample != m_pOutputFormat->wBitsPerSample )
  1061. {
  1062. fConvert = true;
  1063. m_converter.SetInputFormat(&wavFormat);
  1064. }
  1065. }
  1066. // get the start and end points, and clean them up if necessary.
  1067. if ( SUCCEEDED(hr) )
  1068. {
  1069. hr = pIPE->GetStart(&entryFrom);
  1070. if ( SUCCEEDED(hr) )
  1071. {
  1072. if ( entryFrom < 0.0 )
  1073. {
  1074. entryFrom = 0.0;
  1075. }
  1076. hr = pIPE->GetEnd(&entryTo);
  1077. }
  1078. if ( SUCCEEDED(hr) )
  1079. {
  1080. long lDataSize = 0;
  1081. double dEnd = 0.0;
  1082. // make sure we're not trying to read past end of file
  1083. m_pVapiIO->GetDataSize(&lDataSize);
  1084. dEnd = ((double)lDataSize)/wavFormat.nBlockAlign/wavFormat.nSamplesPerSec;
  1085. if ( entryTo > dEnd )
  1086. {
  1087. entryTo = dEnd;
  1088. }
  1089. if ( entryTo != -1.0 && entryFrom >= entryTo )
  1090. {
  1091. entryFrom = entryTo;
  1092. }
  1093. }
  1094. }
  1095. if ( SUCCEEDED(hr) )
  1096. {
  1097. // read the samples
  1098. if ( SUCCEEDED(hr) )
  1099. {
  1100. if ( m_pVapiIO->ReadSamples( entryFrom, entryTo, &pvSamples, &iNumSamples, 1) != 0 )
  1101. {
  1102. hr = E_FAIL;
  1103. }
  1104. if ( iNumSamples == 0 )
  1105. {
  1106. hr = E_FAIL;
  1107. }
  1108. }
  1109. // convert to desired output format
  1110. if ( SUCCEEDED(hr) && fConvert && pvSamples )
  1111. {
  1112. hr = m_converter.ConvertSamples(pvSamples, iNumSamples, (void**) &pvNewSamples, &iNumNewSamples);
  1113. if ( SUCCEEDED(hr) )
  1114. {
  1115. delete [] pvSamples;
  1116. pvSamples = pvNewSamples;
  1117. iNumSamples = iNumNewSamples;
  1118. pvNewSamples = NULL;
  1119. iNumNewSamples = 0;
  1120. }
  1121. }
  1122. // do rate change. Must be format-converted already!
  1123. if ( SUCCEEDED(hr) && m_pTsm )
  1124. {
  1125. // Don't GetActions - the rate flag is kept on since TTS eng needs it too.
  1126. hr = pOutputSite->GetRate( &lRateAdj );
  1127. if ( SUCCEEDED(hr) )
  1128. {
  1129. ComputeRateAdj(lRateAdj, &flRateAdj);
  1130. flRateAdj *= m_flXMLRateAdj;
  1131. m_pTsm->SetScaleFactor( (double) flRateAdj );
  1132. if ( flRateAdj != 1.0 && pvSamples )
  1133. {
  1134. // can't adjust rate for chunks smaller than the frame length
  1135. if ( iNumSamples > m_pTsm->GetFrameLen() )
  1136. {
  1137. hr = m_pTsm->AdjustTimeScale( pvSamples, iNumSamples, &pvNewSamples, &iNumNewSamples, m_pOutputFormat );
  1138. if ( SUCCEEDED(hr) )
  1139. {
  1140. delete [] pvSamples;
  1141. pvSamples = pvNewSamples;
  1142. iNumSamples = iNumNewSamples;
  1143. pvNewSamples = NULL;
  1144. iNumNewSamples = 0;
  1145. }
  1146. }
  1147. }
  1148. }
  1149. }
  1150. // check for volume change
  1151. if ( SUCCEEDED(hr) )
  1152. {
  1153. // Prompt Eng doesn't GetActions - the Vol flag is kept on since TTS eng needs it.
  1154. hr = pOutputSite->GetVolume( &unVol );
  1155. }
  1156. // Multiply the three volume sources
  1157. if ( SUCCEEDED(hr) )
  1158. {
  1159. dVol = ((double) unVol) / 100; // make dVol fractional
  1160. dVol *= m_flEntryGain; // gain from registry
  1161. dVol *= m_flXMLVolume; // gain from XML
  1162. if ( dVol != 1.0 )
  1163. {
  1164. hr = ApplyGain( pvSamples, &pvNewSamples, iNumSamples, dVol );
  1165. // Did ApplyGain need to create a new buffer?
  1166. if ( pvNewSamples )
  1167. {
  1168. // send the volume modified buff instead of the original
  1169. delete [] pvSamples;
  1170. pvSamples = pvNewSamples;
  1171. pvNewSamples = NULL;
  1172. }
  1173. }
  1174. }
  1175. // EVENTS:
  1176. // Send a private engine event at beginning of each prompt
  1177. if ( SUCCEEDED(hr) )
  1178. {
  1179. hr = SendEvent(SPEI_TTS_PRIVATE, pOutputSite, 0, ulTextOffset, ulTextLen);
  1180. }
  1181. // Mid-prompt pausing requires word boundaries. We don't know word-boundaries within
  1182. // prompts, so approximate them by sending a word boundary event for every half-second
  1183. // of audio.
  1184. if ( SUCCEEDED(hr) )
  1185. {
  1186. ULONG ulOffset = 0;
  1187. int iHalfSecs = iNumSamples / m_pOutputFormat->nSamplesPerSec * 2;
  1188. for ( i = 0; i <= iHalfSecs; i++ )
  1189. {
  1190. ulOffset = i * (m_pOutputFormat->nSamplesPerSec / 2) * m_pOutputFormat->nBlockAlign;
  1191. SendEvent(SPEI_WORD_BOUNDARY, pOutputSite, ulOffset, ulTextOffset, ulTextLen);
  1192. }
  1193. }
  1194. // write samples to output site
  1195. if ( SUCCEEDED(hr) )
  1196. {
  1197. hr = ((CLocalTTSEngineSite*)(pOutputSite))->Write(pvSamples, iNumSamples * m_pOutputFormat->nBlockAlign, &pcbWritten);
  1198. if ( SUCCEEDED(hr) )
  1199. {
  1200. ((CLocalTTSEngineSite*)(pOutputSite))->UpdateBytesWritten();
  1201. }
  1202. delete [] pvSamples;
  1203. pvSamples = NULL;
  1204. iNumSamples = 0;
  1205. pcbWritten = 0;
  1206. }
  1207. } // if ( SUCCEEDED(hr) )
  1208. hr = CloseEntryFile();
  1209. } // if ( SUCCEEDED(hr)
  1210. if ( pvSamples )
  1211. {
  1212. delete[]pvSamples;
  1213. pvSamples = NULL;
  1214. iNumSamples = 0;
  1215. }
  1216. SPDBG_REPORT_ON_FAIL( hr );
  1217. return hr;
  1218. }
  1219. //////////////////////////////////////////////////////////////////////
  1220. //
  1221. // All of the following functions are non-interface helpers.
  1222. //
  1223. //////////////////////////////////////////////////////////////////////
  1224. //////////////////////////////////////////////////////////////////////
  1225. // CPromptDb::ComputeRateAdj
  1226. //
  1227. // Computes the rate multiplier.
  1228. //
  1229. /////////////////////////////////////////////////////// JOEM 11-2000 //
  1230. void CPromptDb::ComputeRateAdj(const long lRate, float* flRate)
  1231. {
  1232. SPDBG_FUNC( "CPromptDb::ComputeRateAdj" );
  1233. SPDBG_ASSERT(flRate);
  1234. if ( lRate < 0 )
  1235. {
  1236. if ( lRate < MIN_RATE )
  1237. {
  1238. *flRate = 1.0 / g_dRateScale[0 - MIN_RATE];
  1239. }
  1240. else
  1241. {
  1242. *flRate = 1.0 / g_dRateScale[0 - lRate];
  1243. }
  1244. }
  1245. else
  1246. {
  1247. if ( lRate > MAX_RATE )
  1248. {
  1249. *flRate = g_dRateScale[MAX_RATE];
  1250. }
  1251. else
  1252. {
  1253. *flRate = g_dRateScale[lRate];
  1254. }
  1255. }
  1256. }
  1257. //////////////////////////////////////////////////////////////////////
  1258. // CPromptDb::SendEvent
  1259. //
  1260. // Sends the specified engine event to SAPI.
  1261. //
  1262. ////////////////////////////////////////////////////// JOEM 11-2000 //
  1263. STDMETHODIMP CPromptDb::SendEvent(const SPEVENTENUM eventName, ISpTTSEngineSite* pOutputSite,
  1264. const ULONG ulAudioOffset, const ULONG ulTextOffset, const ULONG ulTextLen)
  1265. {
  1266. SPDBG_ASSERT(pOutputSite);
  1267. SPEVENT event;
  1268. event.eEventId = eventName;
  1269. event.elParamType = SPET_LPARAM_IS_UNDEFINED;
  1270. event.ulStreamNum = 0;
  1271. event.ullAudioStreamOffset = ulAudioOffset; //LocalEngineSite will add prev. bytes
  1272. event.lParam = ulTextOffset;
  1273. event.wParam = ulTextLen;
  1274. return pOutputSite->AddEvents( &event, 1 );
  1275. }
  1276. //////////////////////////////////////////////////////////////////////
  1277. // CPromptDb::ApplyGain
  1278. //
  1279. // Volume adjustment.
  1280. //
  1281. ////////////////////////////////////////////////////// JOEM 01-2001 //
  1282. STDMETHODIMP CPromptDb::ApplyGain(const void* pvInBuff, void** ppvOutBuff, const int iNumSamples, double dGain)
  1283. {
  1284. SPDBG_FUNC( "CPromptDb::ApplyGain" );
  1285. HRESULT hr = S_OK;
  1286. int i = 0;
  1287. SPDBG_ASSERT(pvInBuff);
  1288. SPDBG_ASSERT(iNumSamples);
  1289. // Apply Volume
  1290. if ( SUCCEEDED(hr) && dGain != 1.0 )
  1291. {
  1292. // NOTE THAT REGISTRY GAIN VALUE MAY BE GREATER THAN 1.
  1293. // Make sure it is in bounds of SHRT_MAX, since we'll be multiplying it times samples
  1294. if ( dGain > SHRT_MAX )
  1295. {
  1296. dGain = SHRT_MAX;
  1297. }
  1298. else if ( dGain < 0 ) // gain should never be < 0.
  1299. {
  1300. dGain = 1.0;
  1301. }
  1302. long lGain = ( dGain * (1 << g_iBase) );
  1303. if ( m_pOutputFormat->wFormatTag == WAVE_FORMAT_ALAW || m_pOutputFormat->wFormatTag == WAVE_FORMAT_MULAW )
  1304. {
  1305. short* pnBuff = NULL;
  1306. // need to convert samples
  1307. int iOriginalFormatType = VapiIO::TypeOf (m_pOutputFormat);
  1308. if ( iOriginalFormatType == -1 )
  1309. {
  1310. hr = E_FAIL;
  1311. }
  1312. // Allocate the intermediate buffer
  1313. if ( SUCCEEDED(hr) )
  1314. {
  1315. pnBuff = new short[iNumSamples];
  1316. if ( !pnBuff )
  1317. {
  1318. hr = E_OUTOFMEMORY;
  1319. }
  1320. }
  1321. // Allocate the final (out) buffer
  1322. if ( SUCCEEDED(hr) )
  1323. {
  1324. *ppvOutBuff = new char[iNumSamples * VapiIO::SizeOf(iOriginalFormatType)];
  1325. if ( !*ppvOutBuff )
  1326. {
  1327. hr = E_OUTOFMEMORY;
  1328. }
  1329. }
  1330. // Convert to something we can use
  1331. if ( SUCCEEDED(hr) )
  1332. {
  1333. if ( 0 == VapiIO::DataFormatConversion ((char *)pvInBuff, iOriginalFormatType, (char*)pnBuff, VAPI_PCM16, iNumSamples) )
  1334. {
  1335. hr = E_FAIL;
  1336. }
  1337. }
  1338. // Apply gain
  1339. if ( SUCCEEDED(hr) )
  1340. {
  1341. double dSample = 0;
  1342. for ( i=0; i<iNumSamples; i++ )
  1343. {
  1344. dSample = pnBuff[i] * lGain;
  1345. if ( dSample > LONG_MAX )
  1346. {
  1347. pnBuff[i] = (short) (LONG_MAX >> g_iBase);
  1348. }
  1349. else if ( dSample < LONG_MIN )
  1350. {
  1351. pnBuff[i] = (short) (LONG_MIN >> g_iBase);
  1352. }
  1353. else
  1354. {
  1355. pnBuff[i] = (short) ( ( pnBuff[i] * lGain ) >> g_iBase );
  1356. }
  1357. }
  1358. }
  1359. // convert it back (from intermediate buff to final out buff)
  1360. if ( SUCCEEDED(hr) )
  1361. {
  1362. if ( 0 == VapiIO::DataFormatConversion ((char *)pnBuff, VAPI_PCM16, (char*)*ppvOutBuff, iOriginalFormatType, iNumSamples) )
  1363. {
  1364. hr = E_FAIL;
  1365. }
  1366. }
  1367. if ( pnBuff )
  1368. {
  1369. delete [] pnBuff;
  1370. pnBuff = NULL;
  1371. }
  1372. }
  1373. else if ( m_pOutputFormat->wFormatTag == WAVE_FORMAT_PCM )
  1374. {
  1375. double dSample = 0;
  1376. // no converting necessary
  1377. switch ( m_pOutputFormat->nBlockAlign )
  1378. {
  1379. case 1:
  1380. for ( i=0; i<iNumSamples; i++ )
  1381. {
  1382. for ( i=0; i<iNumSamples; i++ )
  1383. {
  1384. dSample = ((char*)pvInBuff)[i] * lGain;
  1385. if ( dSample > LONG_MAX )
  1386. {
  1387. ((char*)pvInBuff)[i] = (char) (LONG_MAX >> g_iBase);
  1388. }
  1389. else if ( dSample < LONG_MIN )
  1390. {
  1391. ((char*)pvInBuff)[i] = (char) (LONG_MIN >> g_iBase);
  1392. }
  1393. else
  1394. {
  1395. ((char*)pvInBuff)[i] = (char) ( ( ((char*)pvInBuff)[i] * lGain ) >> g_iBase );
  1396. }
  1397. }
  1398. }
  1399. break;
  1400. case 2:
  1401. for ( i=0; i<iNumSamples; i++ )
  1402. {
  1403. dSample = ((short*)pvInBuff)[i] * lGain;
  1404. if ( dSample > LONG_MAX )
  1405. {
  1406. ((short*)pvInBuff)[i] = (short) (LONG_MAX >> g_iBase);
  1407. }
  1408. else if ( dSample < LONG_MIN )
  1409. {
  1410. ((short*)pvInBuff)[i] = (short) (LONG_MIN >> g_iBase);
  1411. }
  1412. else
  1413. {
  1414. ((short*)pvInBuff)[i] = (short) ( ( ((short*)pvInBuff)[i] * lGain ) >> g_iBase );
  1415. }
  1416. }
  1417. break;
  1418. default:
  1419. hr = E_FAIL;
  1420. }
  1421. }
  1422. else
  1423. {
  1424. hr = E_FAIL;
  1425. }
  1426. }
  1427. if ( FAILED(hr) )
  1428. {
  1429. if ( *ppvOutBuff )
  1430. {
  1431. delete [] *ppvOutBuff;
  1432. *ppvOutBuff = NULL;
  1433. }
  1434. }
  1435. SPDBG_REPORT_ON_FAIL( hr );
  1436. return hr;
  1437. }
  1438. //////////////////////////////////////////////////////////////////////
  1439. // CPromptDb::ReadEntry
  1440. //
  1441. // Reads a Db entry from the Db file.
  1442. //
  1443. /////////////////////////////////////////////////////// JOEM 3-2000 //
  1444. HRESULT CPromptDb::ReadEntry(FILE* fp, CPromptEntry** ppEntry)
  1445. {
  1446. SPDBG_FUNC( "CPromptDb::ReadEntry" );
  1447. HRESULT hr = S_OK;
  1448. WCHAR* entryInfo = NULL;
  1449. WCHAR* ptr = NULL;
  1450. WCHAR* tmpTag = NULL;
  1451. WCHAR* start = NULL;
  1452. SHORT state = 0;
  1453. double dEntryInfo = 0.0;
  1454. WCHAR line[1024];
  1455. WCHAR fullPath[_MAX_PATH+1] = L"";
  1456. WCHAR dir[_MAX_DIR] = L"";
  1457. bool fId = false;
  1458. bool fFile = false;
  1459. bool fText = false;
  1460. bool fOriginal = false;
  1461. bool fFrom = false;
  1462. bool fTo = false;
  1463. bool fStart = false;
  1464. bool fEnd = false;
  1465. bool fLeft = false;
  1466. bool fRight = false;
  1467. SPDBG_ASSERT (fp);
  1468. *ppEntry = new CPromptEntry;
  1469. if (!*ppEntry)
  1470. {
  1471. hr = E_OUTOFMEMORY;
  1472. }
  1473. while ( ( state != -1 ) && SUCCEEDED(hr) )
  1474. {
  1475. if ( !fgetws(line, 1024, fp) )
  1476. {
  1477. if (state == 0)
  1478. {
  1479. (*ppEntry)->Release();
  1480. *ppEntry = NULL;
  1481. hr = S_FALSE;
  1482. break; /* No more entries to read */
  1483. }
  1484. else
  1485. {
  1486. hr = PEERR_DB_BAD_FORMAT; /* EOF in the middle of reading an entry*/
  1487. }
  1488. }
  1489. if ( SUCCEEDED(hr) )
  1490. {
  1491. // Strip off the newline character
  1492. if (line[wcslen(line)-1] == L'\n')
  1493. {
  1494. line[wcslen(line)-1] = L'\0';
  1495. }
  1496. // Line ends when a comment marker is found
  1497. if ( (ptr = wcschr (line, L'#')) != NULL ) {
  1498. *ptr = L'\0';
  1499. }
  1500. ptr = line;
  1501. WSkipWhiteSpace (ptr);
  1502. }
  1503. if ( SUCCEEDED(hr) )
  1504. {
  1505. while ( SUCCEEDED(hr) && *ptr )
  1506. {
  1507. switch (state)
  1508. {
  1509. case 0:
  1510. // Search for the starting tag
  1511. // Discard everything up to the starting tag
  1512. if ( wcsncmp( ptr, ENTRY_TAG, sizeof(ENTRY_TAG)/sizeof(ENTRY_TAG[0]) - 1 ) != 0)
  1513. {
  1514. hr = PEERR_DB_BAD_FORMAT;
  1515. }
  1516. if ( SUCCEEDED (hr) )
  1517. {
  1518. ptr += wcslen (ENTRY_TAG);
  1519. state = 1;
  1520. }
  1521. break;
  1522. case 1:
  1523. WSkipWhiteSpace (ptr);
  1524. if (*ptr)
  1525. {
  1526. if ( wcsncmp(ptr, ENTRY_START, sizeof(ENTRY_START)/sizeof(ENTRY_START[0]) - 1 ) ==0 )
  1527. {
  1528. ptr += wcslen (ENTRY_START);
  1529. state = 2;
  1530. }
  1531. else
  1532. {
  1533. hr = PEERR_DB_BAD_FORMAT;
  1534. }
  1535. }
  1536. break;
  1537. case 2:
  1538. // Only one item per line
  1539. if ( (start = wcsstr(ptr, ENTRY_END)) != NULL)
  1540. {
  1541. // If we find the entry_end marker, this is the last
  1542. // line. Remember, this line could have an item also
  1543. *start = L'\0';
  1544. state = -1;
  1545. }
  1546. WSkipWhiteSpace (ptr);
  1547. if (!*ptr)
  1548. {
  1549. break;
  1550. }
  1551. // Must be one of this elements of the entry,
  1552. // otherwise, error
  1553. hr = ExtractString (ptr, ID_TAG, &entryInfo);
  1554. if ( SUCCEEDED (hr) )
  1555. {
  1556. if ( fId )
  1557. {
  1558. hr = PEERR_DB_BAD_FORMAT;
  1559. //SPDBG_ASSERT (! "Duplicate ID field in database entry.");
  1560. }
  1561. if ( SUCCEEDED(hr) )
  1562. {
  1563. hr = (*ppEntry)->SetId(entryInfo);
  1564. }
  1565. fId = true;
  1566. ptr = L"";
  1567. free(entryInfo);
  1568. entryInfo = NULL;
  1569. break;
  1570. }
  1571. hr = ExtractString (ptr, TEXT_TAG, &entryInfo);
  1572. if ( SUCCEEDED (hr) )
  1573. {
  1574. if ( fText )
  1575. {
  1576. hr = PEERR_DB_BAD_FORMAT;
  1577. //SPDBG_ASSERT (! "Duplicate TEXT field in database entry.");
  1578. }
  1579. if ( SUCCEEDED(hr) )
  1580. {
  1581. hr = (*ppEntry)->SetText(entryInfo);
  1582. }
  1583. fText = true;
  1584. ptr = L"";
  1585. free(entryInfo);
  1586. entryInfo = NULL;
  1587. break;
  1588. }
  1589. hr = ExtractString (ptr, ORIGINAL_TAG, &entryInfo);
  1590. if ( SUCCEEDED (hr) )
  1591. {
  1592. if ( fOriginal )
  1593. {
  1594. hr = PEERR_DB_BAD_FORMAT;
  1595. //SPDBG_ASSERT (! "Duplicate TEXT field in database entry.");
  1596. }
  1597. if ( SUCCEEDED(hr) )
  1598. {
  1599. hr = (*ppEntry)->SetOriginalText(entryInfo);
  1600. }
  1601. fOriginal = true;
  1602. ptr = L"";
  1603. free(entryInfo);
  1604. entryInfo = NULL;
  1605. break;
  1606. }
  1607. hr = ExtractString (ptr, FILE_TAG, &entryInfo);
  1608. if ( SUCCEEDED (hr) )
  1609. {
  1610. if ( fFile )
  1611. {
  1612. hr = PEERR_DB_BAD_FORMAT;
  1613. //SPDBG_ASSERT (! "Duplicate FILE NAME field in database entry.");
  1614. }
  1615. if ( SUCCEEDED(hr) )
  1616. {
  1617. if ( entryInfo )
  1618. {
  1619. // Is it a full path? (i.e, does it start with a "\" or is 2nd char a colon)
  1620. if ( wcslen(entryInfo) >= 2 && ( entryInfo[0] == L'\\' || entryInfo[1] == L':' ) )
  1621. {
  1622. hr = (*ppEntry)->SetFileName(entryInfo);
  1623. }
  1624. else // must be a relative path
  1625. {
  1626. // Construct the full path to the entry
  1627. _wsplitpath(m_pActiveDb->pszPathName, fullPath, dir, NULL, NULL);
  1628. wcscat(fullPath, dir);
  1629. wcscat(fullPath, entryInfo);
  1630. // Don't check this path here. We don't want Db loading to fail
  1631. // if a wav doesn't exist -- load the db anyway, and later when
  1632. // searching db, we check the paths and resort to TTS if path is invalid.
  1633. hr = (*ppEntry)->SetFileName(fullPath);
  1634. }
  1635. }
  1636. }
  1637. fFile = true;
  1638. ptr = L"";
  1639. free(entryInfo);
  1640. entryInfo = NULL;
  1641. break;
  1642. }
  1643. hr = ExtractString (ptr, TAG_TAG, &entryInfo);
  1644. if ( SUCCEEDED (hr) )
  1645. {
  1646. hr = (*ppEntry)->AddTag(entryInfo);
  1647. ptr = L"";
  1648. free(entryInfo);
  1649. entryInfo = NULL;
  1650. break;
  1651. }
  1652. hr = ExtractString (ptr, START_PHONE_TAG, &entryInfo);
  1653. if ( SUCCEEDED (hr) )
  1654. {
  1655. if ( fStart )
  1656. {
  1657. hr = PEERR_DB_BAD_FORMAT;
  1658. //SPDBG_ASSERT (! "Duplicate START PHONE field in database entry.");
  1659. }
  1660. if ( SUCCEEDED(hr) )
  1661. {
  1662. hr = (*ppEntry)->SetStartPhone(entryInfo);
  1663. }
  1664. fStart = true;
  1665. ptr = L"";
  1666. free(entryInfo);
  1667. entryInfo = NULL;
  1668. break;
  1669. }
  1670. hr = ExtractString (ptr, END_PHONE_TAG, &entryInfo);
  1671. if ( SUCCEEDED (hr) )
  1672. {
  1673. if ( fEnd )
  1674. {
  1675. hr = PEERR_DB_BAD_FORMAT;
  1676. //SPDBG_ASSERT (! "Duplicate END PHONE field in database entry.");
  1677. }
  1678. if ( SUCCEEDED(hr) )
  1679. {
  1680. hr = (*ppEntry)->SetEndPhone(entryInfo);
  1681. }
  1682. fEnd = true;
  1683. ptr = L"";
  1684. free(entryInfo);
  1685. entryInfo = NULL;
  1686. break;
  1687. }
  1688. hr = ExtractString (ptr, RIGHT_CONTEXT_TAG, &entryInfo);
  1689. if ( SUCCEEDED (hr) )
  1690. {
  1691. if ( fRight )
  1692. {
  1693. hr = PEERR_DB_BAD_FORMAT;
  1694. //SPDBG_ASSERT (! "Duplicate RIGHT CONTEXT field in database entry.");
  1695. }
  1696. if ( SUCCEEDED(hr) )
  1697. {
  1698. hr = (*ppEntry)->SetRightContext(entryInfo);
  1699. }
  1700. fRight = true;
  1701. ptr = L"";
  1702. free(entryInfo);
  1703. entryInfo = NULL;
  1704. break;
  1705. }
  1706. hr = ExtractString (ptr, LEFT_CONTEXT_TAG, &entryInfo);
  1707. if ( SUCCEEDED (hr) )
  1708. {
  1709. if ( fLeft )
  1710. {
  1711. hr = PEERR_DB_BAD_FORMAT;
  1712. //SPDBG_ASSERT (! "Duplicate LEFT CONTEXT field in database entry.");
  1713. }
  1714. if ( SUCCEEDED(hr) )
  1715. {
  1716. hr = (*ppEntry)->SetLeftContext(entryInfo);
  1717. }
  1718. fLeft = true;
  1719. ptr = L"";
  1720. free(entryInfo);
  1721. entryInfo = NULL;
  1722. break;
  1723. }
  1724. hr = ExtractDouble (ptr, FROM_TAG, &dEntryInfo);
  1725. if ( SUCCEEDED (hr) )
  1726. {
  1727. if ( fFrom )
  1728. {
  1729. hr = PEERR_DB_BAD_FORMAT;
  1730. //SPDBG_ASSERT (! "Duplicate FROM field in database entry.");
  1731. }
  1732. if ( SUCCEEDED(hr) )
  1733. {
  1734. hr = (*ppEntry)->SetStart(dEntryInfo);
  1735. }
  1736. fFrom = true;
  1737. ptr = L"";
  1738. break;
  1739. }
  1740. hr = ExtractDouble (ptr, TO_TAG, &dEntryInfo);
  1741. if ( SUCCEEDED (hr) )
  1742. {
  1743. if ( fTo )
  1744. {
  1745. hr = PEERR_DB_BAD_FORMAT;
  1746. //SPDBG_ASSERT (! "Duplicate TO field in database entry.");
  1747. }
  1748. if ( SUCCEEDED(hr) )
  1749. {
  1750. hr = (*ppEntry)->SetEnd(dEntryInfo);
  1751. }
  1752. fTo = true;
  1753. ptr = L"";
  1754. break;
  1755. }
  1756. hr = PEERR_DB_BAD_FORMAT;
  1757. } // switch (state)
  1758. } // while (*ptr)
  1759. }
  1760. } // while ( SUCCEEDED(hr) && state != -1)
  1761. // THESE ARE THE MANDATORY FIELDS FOR THE DATABASE ENTRY
  1762. if ( *ppEntry && SUCCEEDED(hr) )
  1763. {
  1764. if ( !fId )
  1765. {
  1766. //SPDBG_ASSERT (! "Missing ID field in database entry.");
  1767. hr = PEERR_DB_BAD_FORMAT;
  1768. }
  1769. if ( !fFile )
  1770. {
  1771. //SPDBG_ASSERT (! "Missing FILE field in database entry.");
  1772. hr = PEERR_DB_BAD_FORMAT;
  1773. }
  1774. if ( !fText )
  1775. {
  1776. //SPDBG_ASSERT (! "Missing TEXT field in database entry.");
  1777. hr = PEERR_DB_BAD_FORMAT;
  1778. }
  1779. if ( !fFrom )
  1780. {
  1781. //SPDBG_ASSERT (! "Missing FROM field in database entry.");
  1782. hr = PEERR_DB_BAD_FORMAT;
  1783. }
  1784. if ( !fTo )
  1785. {
  1786. //SPDBG_ASSERT (! "Missing TO field in database entry.");
  1787. hr = PEERR_DB_BAD_FORMAT;
  1788. }
  1789. }
  1790. // From is less than To?
  1791. if ( *ppEntry && SUCCEEDED(hr) )
  1792. {
  1793. double from = 0.0;
  1794. double to = 0.0;
  1795. hr = (*ppEntry)->GetStart(&from);
  1796. if ( SUCCEEDED(hr) )
  1797. {
  1798. hr = (*ppEntry)->GetEnd(&to);
  1799. }
  1800. if ( SUCCEEDED(hr) )
  1801. {
  1802. if ( to <= from )
  1803. {
  1804. // a value of -1.0 is ok for "to" -- it means play to end of file.
  1805. if ( to != -1.0 )
  1806. {
  1807. hr = PEERR_DB_BAD_FORMAT;
  1808. }
  1809. }
  1810. }
  1811. }
  1812. if ( FAILED(hr) && *ppEntry )
  1813. {
  1814. (*ppEntry)->Release();
  1815. *ppEntry = NULL;
  1816. }
  1817. SPDBG_REPORT_ON_FAIL( hr );
  1818. return hr;
  1819. }
  1820. //////////////////////////////////////////////////////////////////////
  1821. // CPromptDb::WriteEntry
  1822. //
  1823. // Writes a formated entry to a Db file.
  1824. //
  1825. /////////////////////////////////////////////////////// JOEM 3-2000 //
  1826. HRESULT CPromptDb::WriteEntry(FILE *fp, IPromptEntry *pIPE)
  1827. {
  1828. SPDBG_FUNC( "CPromptDb::WriteEntry" );
  1829. HRESULT hr = S_OK;
  1830. const WCHAR* entryText = NULL;
  1831. SPDBG_ASSERT (fp);
  1832. SPDBG_ASSERT (pIPE);
  1833. fwprintf (fp, L"%s %s\n", ENTRY_TAG, ENTRY_START);
  1834. if ( SUCCEEDED(hr) )
  1835. {
  1836. hr = pIPE->GetId(&entryText);
  1837. if ( SUCCEEDED(hr) )
  1838. {
  1839. fwprintf (fp, L"\t%s %s\n", ID_TAG, entryText);
  1840. entryText = NULL;
  1841. }
  1842. }
  1843. if ( SUCCEEDED(hr) )
  1844. {
  1845. hr = pIPE->GetText(&entryText);
  1846. if ( SUCCEEDED(hr) )
  1847. {
  1848. fwprintf (fp, L"\t%s %s\n", TEXT_TAG, entryText);
  1849. entryText = NULL;
  1850. }
  1851. }
  1852. if ( SUCCEEDED(hr) )
  1853. {
  1854. hr = pIPE->GetOriginalText(&entryText);
  1855. if ( SUCCEEDED(hr) )
  1856. {
  1857. fwprintf (fp, L"\t%s %s\n", ORIGINAL_TAG, entryText);
  1858. entryText = NULL;
  1859. }
  1860. }
  1861. if ( SUCCEEDED(hr) )
  1862. {
  1863. USHORT entryTagCount = 0;
  1864. hr = pIPE->CountTags(&entryTagCount);
  1865. if ( SUCCEEDED(hr) && entryTagCount )
  1866. {
  1867. for ( USHORT i=0; i < entryTagCount; i++)
  1868. {
  1869. hr = pIPE->GetTag(&entryText, i);
  1870. if ( SUCCEEDED(hr) )
  1871. {
  1872. fwprintf( fp, L"\t%s %s\n", TAG_TAG, entryText );
  1873. entryText = NULL;
  1874. }
  1875. }
  1876. }
  1877. }
  1878. if ( SUCCEEDED(hr) )
  1879. {
  1880. hr = pIPE->GetFileName(&entryText);
  1881. if ( SUCCEEDED(hr) )
  1882. {
  1883. fwprintf (fp, L"\t%s %s\n", FILE_TAG, entryText);
  1884. entryText = NULL;
  1885. }
  1886. }
  1887. if ( SUCCEEDED(hr) )
  1888. {
  1889. double start = 0.0;
  1890. hr = pIPE->GetStart(&start);
  1891. if ( SUCCEEDED(hr) )
  1892. {
  1893. fwprintf( fp, L"\t%s %f\n", FROM_TAG, start);
  1894. }
  1895. }
  1896. if ( SUCCEEDED(hr) )
  1897. {
  1898. double end = 0.0;
  1899. hr = pIPE->GetEnd(&end);
  1900. if ( SUCCEEDED(hr) )
  1901. {
  1902. fwprintf( fp, L"\t%s %f\n", TO_TAG, end);
  1903. }
  1904. }
  1905. fwprintf (fp, L"%s\n\n", ENTRY_END);
  1906. SPDBG_REPORT_ON_FAIL( hr );
  1907. return hr;
  1908. }
  1909. //////////////////////////////////////////////////////////////////////
  1910. // CPromptDb::DuplicateEntry
  1911. //
  1912. //
  1913. /////////////////////////////////////////////////////// JOEM 3-2000 //
  1914. CPromptEntry* CPromptDb::DuplicateEntry(const CPromptEntry *oldEntry)
  1915. {
  1916. SPDBG_FUNC( "CPromptDb::DuplicateEntry" );
  1917. CPromptEntry* newEntry = new CPromptEntry(*oldEntry);
  1918. return newEntry;
  1919. }
  1920. //////////////////////////////////////////////////////////////////////
  1921. // CPromptDb::ExtractString
  1922. //
  1923. //
  1924. /////////////////////////////////////////////////////// JOEM 3-2000 //
  1925. HRESULT CPromptDb::ExtractString(WCHAR *line, const WCHAR *tag, WCHAR **value)
  1926. {
  1927. SPDBG_FUNC( "CPromptDb::ExtractString" );
  1928. HRESULT hr = S_OK;
  1929. int tagLen = 0;
  1930. int entryLen = 0;
  1931. tagLen = (USHORT) wcslen (tag);
  1932. if (wcsncmp(line, tag, tagLen)==0)
  1933. {
  1934. line += tagLen;
  1935. WSkipWhiteSpace (line);
  1936. for (entryLen = wcslen(line) -1; entryLen>=0 && iswspace(line[entryLen]); entryLen--)
  1937. {
  1938. line[entryLen] = L'\0';
  1939. }
  1940. // Control characters are not allowed in Db
  1941. if ( FindUnicodeControlChar(line) )
  1942. {
  1943. hr = E_UNEXPECTED;
  1944. }
  1945. if ( SUCCEEDED(hr) && !wcslen(line) )
  1946. {
  1947. hr = E_INVALIDARG;
  1948. }
  1949. if ( SUCCEEDED(hr) )
  1950. {
  1951. if ((*value = _wcsdup(line)) == NULL)
  1952. {
  1953. hr = E_OUTOFMEMORY;
  1954. }
  1955. }
  1956. }
  1957. else
  1958. {
  1959. hr = E_FAIL;
  1960. }
  1961. // Don't report failures here - this func is allowed to fail frequently.
  1962. //SPDBG_REPORT_ON_FAIL( hr );
  1963. return hr;
  1964. }
  1965. //////////////////////////////////////////////////////////////////////
  1966. // CPromptDb::ExtractDouble
  1967. //
  1968. //
  1969. /////////////////////////////////////////////////////// JOEM 3-2000 //
  1970. HRESULT CPromptDb::ExtractDouble(WCHAR *line, const WCHAR *tag, double *value)
  1971. {
  1972. SPDBG_FUNC( "CPromptDb::ExtractDouble" );
  1973. HRESULT hr = S_OK;
  1974. int tagLen = 0;
  1975. int entryLen = 0;
  1976. WCHAR* psz = NULL;
  1977. *value = 0.0;
  1978. tagLen = (USHORT) wcslen (tag);
  1979. if (wcsncmp(line, tag, tagLen)==0)
  1980. {
  1981. line += tagLen;
  1982. WSkipWhiteSpace (line);
  1983. for (entryLen = wcslen(line) -1; entryLen>=0 && iswspace(line[entryLen]); entryLen--)
  1984. {
  1985. line[entryLen] = L'\0';
  1986. }
  1987. // Control characters are not allowed in Db
  1988. if ( FindUnicodeControlChar(line) )
  1989. {
  1990. hr = E_UNEXPECTED;
  1991. }
  1992. if ( SUCCEEDED(hr) && !wcslen(line) )
  1993. {
  1994. hr = E_INVALIDARG;
  1995. }
  1996. // make sure it's a number
  1997. if ( SUCCEEDED(hr) )
  1998. {
  1999. psz = line;
  2000. while ( SUCCEEDED(hr) && psz[0] )
  2001. {
  2002. if ( iswdigit(psz[0]) || psz[0] == L'.' || psz[0] == L'-' )
  2003. {
  2004. psz++;
  2005. }
  2006. else
  2007. {
  2008. hr = E_INVALIDARG;
  2009. }
  2010. }
  2011. }
  2012. if ( SUCCEEDED(hr) )
  2013. {
  2014. *value = wcstod( line, NULL );
  2015. }
  2016. if ( SUCCEEDED(hr) && *value < 0 && *value != -1.0 )
  2017. {
  2018. hr = E_INVALIDARG;
  2019. }
  2020. }
  2021. else
  2022. {
  2023. hr = E_FAIL;
  2024. }
  2025. // Don't report failures here - this func is allowed to fail frequently.
  2026. //SPDBG_REPORT_ON_FAIL( hr );
  2027. return hr;
  2028. }
  2029. //////////////////////////////////////////////////////////////////////
  2030. // CPromptDb::TempName
  2031. //
  2032. // Creates a temporary file name for the active db.
  2033. //
  2034. /////////////////////////////////////////////////////// JOEM 3-2000 //
  2035. HRESULT CPromptDb::TempName()
  2036. {
  2037. SPDBG_FUNC( "CPromptDb::TempName" );
  2038. HRESULT hr = S_OK;
  2039. WCHAR nameStr[256];
  2040. SPDBG_ASSERT( m_pActiveDb );
  2041. SPDBG_ASSERT( m_pActiveDb->pszPathName );
  2042. wcscat( wcscpy( nameStr, m_pActiveDb->pszPathName ), L".tmp" );
  2043. if ( m_pActiveDb->pszTempName && (wcscmp( m_pActiveDb->pszTempName, nameStr ) != 0) )
  2044. {
  2045. free (m_pActiveDb->pszTempName);
  2046. m_pActiveDb->pszTempName = NULL;
  2047. }
  2048. if ( !m_pActiveDb->pszTempName )
  2049. {
  2050. if ( ( m_pActiveDb->pszTempName = _wcsdup(nameStr) ) == NULL)
  2051. {
  2052. hr = E_OUTOFMEMORY;
  2053. }
  2054. }
  2055. SPDBG_REPORT_ON_FAIL( hr );
  2056. return hr;
  2057. }
  2058. //////////////////////////////////////////////////////////////////////
  2059. // CPromptDb::IndexTextHash
  2060. //
  2061. // Makes a text hash. Key: text
  2062. // Value: list of IDs for that text
  2063. //
  2064. /////////////////////////////////////////////////////// JOEM 3-2000 //
  2065. HRESULT CPromptDb::IndexTextHash()
  2066. {
  2067. SPDBG_FUNC( "CPromptDb::IndexTextHash" );
  2068. HRESULT hr = S_OK;
  2069. WCHAR* id = NULL;
  2070. const WCHAR* entryInfo = NULL;
  2071. USHORT idx1 = 0;
  2072. USHORT idx2 = 0;
  2073. USHORT i = 0;
  2074. USHORT found = 0;
  2075. CPromptEntry* entry = NULL;
  2076. CDynStrArray* candList = NULL;
  2077. while ( SUCCEEDED(hr) )
  2078. {
  2079. m_pActiveDb->idHash.NextKey (&idx1, &idx2, &id);
  2080. if ( !id )
  2081. {
  2082. // this was the last one
  2083. break;
  2084. }
  2085. if ( SUCCEEDED(hr) )
  2086. {
  2087. entry = (CPromptEntry*) m_pActiveDb->idHash.Find(id);
  2088. if ( !entry )
  2089. {
  2090. hr = E_UNEXPECTED;
  2091. }
  2092. }
  2093. if ( SUCCEEDED(hr) )
  2094. {
  2095. hr = entry->GetText(&entryInfo);
  2096. if ( SUCCEEDED(hr) )
  2097. {
  2098. // Is there already a list of entries for this text? If not, create one.
  2099. if ( (candList = (CDynStrArray*) m_pActiveDb->textHash.Find(entryInfo)) == NULL )
  2100. {
  2101. candList = new CDynStrArray;
  2102. if (!candList)
  2103. {
  2104. hr = E_OUTOFMEMORY;
  2105. }
  2106. if ( SUCCEEDED(hr) )
  2107. {
  2108. hr = m_pActiveDb->textHash.BuildEntry(entryInfo, candList);
  2109. }
  2110. }
  2111. }
  2112. entryInfo = NULL;
  2113. if ( SUCCEEDED(hr) )
  2114. {
  2115. // Search the list, check that the entry doesn't exist
  2116. found = 0;
  2117. hr = entry->GetId(&entryInfo);
  2118. if ( SUCCEEDED(hr) )
  2119. {
  2120. for ( i = 0; i < candList->m_aDstr.GetSize(); i++ )
  2121. {
  2122. if ( wcscmp(candList->m_aDstr[i].dstr, entryInfo) == 0 )
  2123. {
  2124. found = 1;
  2125. break;
  2126. }
  2127. }
  2128. if ( !found )
  2129. {
  2130. // Add item to the list, the list is already in the hash table
  2131. candList->m_aDstr.Add(entryInfo);
  2132. }
  2133. entryInfo = NULL;
  2134. }
  2135. }
  2136. entry = NULL;
  2137. } // if ( SUCCEEDED(hr) )
  2138. } // while
  2139. if ( FAILED(hr) )
  2140. {
  2141. if ( candList )
  2142. {
  2143. delete candList;
  2144. candList = NULL;
  2145. }
  2146. }
  2147. return hr;
  2148. }
  2149. //////////////////////////////////////////////////////////////////////
  2150. // CPromptDb::GetPromptFormat
  2151. //
  2152. // Gets the format of the first prompt file, used for setting the
  2153. // output format. Note, however, that the application can change the
  2154. // format, and that prompt files do not need to be all in the same
  2155. // format.
  2156. //
  2157. /////////////////////////////////////////////////////// JOEM 3-2000 //
  2158. STDMETHODIMP CPromptDb::GetPromptFormat (WAVEFORMATEX **ppwf)
  2159. {
  2160. HRESULT hr = E_FAIL;
  2161. USHORT unId1 = 0;
  2162. USHORT unId2 = 0;
  2163. WCHAR *pszId = NULL;
  2164. IPromptEntry* pIPE = NULL;
  2165. while ( FAILED(hr) )
  2166. {
  2167. hr = GetNextEntry(&unId1, &unId2, &pIPE);
  2168. }
  2169. if ( hr == S_OK )
  2170. {
  2171. WAVEFORMATEX* newWF = (WAVEFORMATEX*)::CoTaskMemAlloc( sizeof(WAVEFORMATEX) );
  2172. if ( newWF )
  2173. {
  2174. hr = pIPE->GetFormat( &newWF );
  2175. newWF->cbSize = 0;
  2176. }
  2177. else
  2178. {
  2179. hr = E_OUTOFMEMORY;
  2180. }
  2181. if ( SUCCEEDED(hr) )
  2182. {
  2183. *ppwf = newWF;
  2184. }
  2185. }
  2186. return hr;
  2187. }