windows-nt/Source/XPSP1/NT/enduser/speech/tts/prompts/engine/promptdb.cpp

2480 lines
69 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//////////////////////////////////////////////////////////////////////
// PromptDb.cpp : Implementation of CPromptDb
//
// Created by JOEM 03-2000
// Copyright (C) 2000 Microsoft Corporation
// All Rights Reserved
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
#include "stdafx.h"
#include "PromptDb.h"
#include "LocalTTSEngineSite.h"
#include "vapiIO.h"
#include <LIMITS>
//#define ENTRY_TAG L"DB_ENTRY"
const WCHAR ENTRY_TAG[] = L"DB_ENTRY";
const WCHAR ENTRY_START[] = L"{";
const WCHAR ENTRY_END[] = L"}";
const WCHAR ID_TAG[] = L"ID:";
const WCHAR TEXT_TAG[] = L"TEXT:";
const WCHAR ORIGINAL_TAG[] = L"ORIGINAL:";
const WCHAR TAG_TAG[] = L"TAG:";
const WCHAR FILE_TAG[] = L"FILE:";
const WCHAR FROM_TAG[] = L"FROM:";
const WCHAR TO_TAG[] = L"TO:";
const WCHAR START_PHONE_TAG[] = L"START_PHONE:";
const WCHAR END_PHONE_TAG[] = L"END_PHONE:";
const WCHAR RIGHT_CONTEXT_TAG[] = L"RIGHT_CONTEXT:";
const WCHAR LEFT_CONTEXT_TAG[] = L"LEFT_CONTEXT:";
const int g_iBase = 16;
//////////////////////////////////////////////////////////////////////
// Db
//
// Construction/Destruction
/////////////////////////////////////////////////////// JOEM 3-2000 //
CDb::CDb()
{
pszPathName = NULL;
pszTempName = NULL;
pszLogicalName = NULL;
}
CDb::~CDb()
{
if ( pszPathName )
{
free ( pszPathName );
pszPathName = NULL;
}
if ( pszTempName )
{
free ( pszTempName );
pszTempName = NULL;
}
if ( pszLogicalName )
{
free ( pszLogicalName );
pszLogicalName = NULL;
}
}
//////////////////////////////////////////////////////////////////////
// CPromptDb
//
// Construction/Destruction
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CPromptDb::FinalConstruct()
{
SPDBG_FUNC( "CPromptDb::FinalConstruct" );
HRESULT hr = S_OK;
m_pActiveDb = NULL;
m_unIndex = 0;
m_vcRef = 1;
m_pOutputFormatId = NULL;
m_pOutputFormat = NULL;
m_flEntryGain = 1.0; // This will be set in registry.
m_flXMLVolume = 1.0; // Full volume unless XML changes it.
m_flXMLRateAdj = 1.0; // Regular rate unless XML changes it.
if ( SUCCEEDED( hr ) )
{
m_pVapiIO = VapiIO::ClassFactory();
if ( !m_pVapiIO )
{
hr = E_OUTOFMEMORY;
}
}
// create the rate changer
if ( SUCCEEDED(hr) )
{
m_pTsm = new CTsm( g_dRateScale[BASE_RATE] );
if ( !m_pTsm )
{
hr = E_OUTOFMEMORY;
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
void CPromptDb::FinalRelease()
{
USHORT i = 0;
m_pActiveDb = NULL;
for ( i=0; i < m_apDbList.GetSize(); i++ )
{
if ( m_apDbList[i] )
{
delete m_apDbList[i];
m_apDbList[i] = NULL;
}
}
m_apDbList.RemoveAll();
for ( i=0; i<m_aSearchList.GetSize(); i++ )
{
m_aSearchList[i].dstr.Clear();
}
m_aSearchList.RemoveAll();
if ( m_pVapiIO )
{
delete m_pVapiIO;
m_pVapiIO = NULL;
}
if ( m_pTsm )
{
delete m_pTsm;
m_pTsm = NULL;
}
if ( m_pOutputFormat )
{
free(m_pOutputFormat);
m_pOutputFormat = NULL;
m_pOutputFormatId = NULL;
}
}
//
// IUnknown implementations
//
//////////////////////////////////////////////////////////////////////
// CPromptDb::QueryInterface
//
/////////////////////////////////////////////////////// JOEM 4-2000 //
STDMETHODIMP CPromptDb::QueryInterface(const IID& iid, void** ppv)
{
if ( iid == IID_IUnknown )
{
*ppv = (IPromptDb*) this;
}
else if ( iid == IID_IPromptDb )
{
*ppv = (IPromptDb*) this;
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
((IUnknown*) *ppv)->AddRef();
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::AddRef
//
/////////////////////////////////////////////////////// JOEM 4-2000 //
ULONG CPromptDb::AddRef()
{
return InterlockedIncrement(&m_vcRef);
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::Release
//
/////////////////////////////////////////////////////// JOEM 4-2000 //
ULONG CPromptDb::Release()
{
if ( 0 == InterlockedDecrement(&m_vcRef) )
{
delete this;
return 0;
}
return (ULONG) m_vcRef;
}
//
// IPromptDb INTERFACE FUNCTION IMPLEMENTATIONS
//
//////////////////////////////////////////////////////////////////////
// CPromptDb::NewDb
//
// Creates a new Db.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::NewDb(const WCHAR* logicalName, const WCHAR* pathName)
{
SPDBG_FUNC( "CPromptDb::NewDb" );
HRESULT hr = S_OK;
CDb* db = NULL;
// See if this Db already exists.
hr = ActivateDbName(logicalName);
if ( SUCCEEDED(hr) )
{
return E_INVALIDARG;
}
db = new CDb;
if ( db )
{
hr = S_OK;
}
if ( SUCCEEDED(hr) )
{
db->pszLogicalName = _wcsdup(logicalName);
if ( !db->pszLogicalName )
{
hr = E_OUTOFMEMORY;
}
}
if ( SUCCEEDED(hr) )
{
db->pszPathName = _wcsdup(pathName);
if ( !db->pszPathName )
{
hr = E_OUTOFMEMORY;
}
}
if ( SUCCEEDED(hr) )
{
m_apDbList.Add(db);
}
if ( FAILED(hr) )
{
if ( db )
{
delete db;
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::AddDb
//
// Adds a Db to the list, and activates it if desired.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::AddDb(const WCHAR* logicalName, const WCHAR* pathName, const WCHAR* pszIdSet, const USHORT loadOption)
{
SPDBG_FUNC( "CPromptDb::AddDb" );
HRESULT hr = S_OK;
CDb* pActiveDb = m_pActiveDb;
if ( !pathName || !wcslen(pathName) )
{
hr = E_INVALIDARG;
}
if ( SUCCEEDED(hr) )
{
if ( !FileExist( pathName ) )
{
hr = PEERR_DB_NOT_FOUND;
}
}
if ( SUCCEEDED(hr) )
{
// if there is logicalName specified, activate or create it.
if ( logicalName )
{
hr = ActivateDbName(logicalName);
if ( FAILED(hr) )
{
hr = NewDb(logicalName, pathName);
if ( SUCCEEDED(hr) )
{
hr = ActivateDbName(logicalName);
}
}
}
else
{
// if no current Db, activate the default
if ( !m_pActiveDb )
{
hr = ActivateDbName(L"DEFAULT");
}
}
}
// Set the Active Db's new path name
if ( SUCCEEDED(hr) )
{
if ( m_pActiveDb->pszPathName )
{
free(m_pActiveDb->pszPathName);
m_pActiveDb->pszPathName = wcsdup(pathName);
if ( !m_pActiveDb->pszPathName )
{
hr = E_OUTOFMEMORY;
}
}
}
// Load it?
if ( SUCCEEDED(hr) )
{
if ( loadOption == DB_LOAD )
{
if ( SUCCEEDED( hr ) )
{
hr = LoadDb(pszIdSet);
}
}
}
// Reset the active Db
m_pActiveDb = pActiveDb;
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::LoadDb
//
// Initializes the hash tables for a new Db.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::LoadDb(const WCHAR* pszIdSet)
{
SPDBG_FUNC( "CPromptDb::LoadDb" );
HRESULT hr = S_OK;
FILE* fp = NULL;
SPDBG_ASSERT( m_pActiveDb );
SPDBG_ASSERT( m_pActiveDb->pszPathName );
fp = _wfopen (m_pActiveDb->pszPathName, L"r");
if ( !fp )
{
hr = E_ACCESSDENIED;
}
if ( SUCCEEDED(hr) )
{
hr = LoadIdHash(fp, pszIdSet);
fclose (fp);
}
if ( SUCCEEDED(hr) )
{
hr = IndexTextHash();
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::UnloadDb
//
// Deactivates and unloads a Db. Makes the default Db active, or the
// first available Db if the default doesn't exist.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::UnloadDb(const WCHAR* pszLogicalName)
{
SPDBG_FUNC( "CPromptDb::UnloadDb" );
HRESULT hr = S_OK;
USHORT i = 0;
USHORT dbIndex = 0;
SPDBG_ASSERT(pszLogicalName);
for ( i=0; i<m_apDbList.GetSize(); i++ )
{
if ( wcscmp(pszLogicalName, m_apDbList[i]->pszLogicalName) == 0 )
{
if ( wcscmp(pszLogicalName, m_pActiveDb->pszLogicalName) == 0 )
{
m_pActiveDb = NULL;
}
delete m_apDbList[i];
m_apDbList[i] = NULL;
m_apDbList.RemoveAt( i );
}
}
if ( !m_pActiveDb )
{
hr = ActivateDbName(L"DEFAULT");
if ( FAILED(hr) )
{
hr = ActivateDbNumber(0);
}
if( FAILED(hr) )
{
hr = S_FALSE;
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::ActivateDbName
//
// Activates the Db specified by logical name.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::ActivateDbName(const WCHAR* pszLogicalName)
{
SPDBG_FUNC( "CPromptDb::ActivateDbName" );
HRESULT hr = PEERR_DB_NOT_FOUND;
WCHAR* pszDb = NULL;
USHORT unSize = 0;
USHORT i = 0;
SPDBG_ASSERT(pszLogicalName);
if ( m_pActiveDb )
{
// See if the Db is already the active one
if ( !wcscmp(m_pActiveDb->pszLogicalName, pszLogicalName ) )
{
hr = S_OK;
}
}
// Did the current Db match?
if ( FAILED(hr) )
{
unSize = (USHORT) m_apDbList.GetSize();
for (i = 0; i < unSize; i++)
{
if ( _wcsicmp(pszLogicalName, m_apDbList[i]->pszLogicalName) == 0 )
{
m_pActiveDb = m_apDbList[i];
hr = S_OK;
}
}
}
// Don't report failures here - this func is allowed to fail frequently.
//SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::ActivateDbNumber
//
// Activates the Db specified by number.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::ActivateDbNumber(const USHORT unIndex)
{
SPDBG_FUNC( "CPromptDb::ActivateDbNumber" );
HRESULT hr = S_OK;
USHORT unSize = 0;
USHORT i = 0;
unSize = (USHORT) m_apDbList.GetSize();
if ( unIndex >= unSize )
{
hr = PEERR_DB_NOT_FOUND;
}
if ( SUCCEEDED(hr) )
{
m_pActiveDb = m_apDbList[unIndex];
if ( !m_pActiveDb )
{
hr = E_UNEXPECTED;
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::UpdateDb
//
// When creating a Db file, this updates the real Db file with the
// contents of the temp file.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::UpdateDb(WCHAR* pszPath)
{
SPDBG_FUNC( "CPromptDb::UpdateDb" );
HRESULT hr = S_OK;
FILE* fp = NULL;
WCHAR* id = NULL;
CPromptEntry* value = NULL;
USHORT idx1 = 0;
USHORT idx2 = 0;
WCHAR backupName[_MAX_PATH + 1];
SPDBG_ASSERT( m_pActiveDb );
// New name?
if ( pszPath )
{
if ( m_pActiveDb->pszPathName )
{
free( m_pActiveDb->pszPathName);
}
m_pActiveDb->pszPathName = wcsdup(pszPath);
if ( !m_pActiveDb->pszPathName )
{
hr = E_OUTOFMEMORY;
}
}
// Create the temp file name
if ( SUCCEEDED(hr) )
{
hr = TempName();
}
// Write the entries to the temp file
if ( SUCCEEDED(hr) )
{
if ((fp = _wfopen(m_pActiveDb->pszTempName, L"w")) == NULL)
{
hr = E_ACCESSDENIED;
}
if ( SUCCEEDED(hr) )
{
fwprintf (fp, L"#\n# PROMPT Database, generated automatically\n#\n\n");
while ( SUCCEEDED(hr) )
{
hr = m_pActiveDb->idHash.NextKey(&idx1, &idx2, &id);
if ( id == NULL )
{
break;
}
if ( SUCCEEDED(hr) )
{
value = (CPromptEntry *) m_pActiveDb->idHash.Find(id);
if ( !value )
{
hr = E_FAIL;
}
}
if ( SUCCEEDED(hr) )
{
hr = WriteEntry (fp, value);
}
}
fclose (fp);
}
}
// Now backup the old Db file
if ( SUCCEEDED(hr) )
{
// Make a backupName
wcscat( wcsncpy( backupName, m_pActiveDb->pszPathName, _MAX_PATH-1 ), L"~" );
// This "remove" is necessary, since the "rename"
// would fail if the file exists.
if ( FileExist( backupName ) )
{
_wremove (backupName);
}
if ( FileExist( m_pActiveDb->pszPathName) )
{
if ( _wrename( m_pActiveDb->pszPathName, backupName ) !=0 )
{
hr = E_ACCESSDENIED;
}
}
}
if ( SUCCEEDED(hr) )
{
if ( _wrename( m_pActiveDb->pszTempName, m_pActiveDb->pszPathName ) !=0 )
{
hr = E_ACCESSDENIED;
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb
//
// CountDb
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::CountDb(USHORT *unCount)
{
SPDBG_FUNC( "CPromptDb::CountDb" );
*unCount = (USHORT) m_apDbList.GetSize();
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::LoadIdHash
//
// Makes a hash table for entry ID numbers, using unique key values.
// Adds all the Db entry ids to the table.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CPromptDb::LoadIdHash(FILE *fp, const WCHAR* pszIdSet)
{
SPDBG_FUNC( "CPromptDb::LoadIdHash" );
HRESULT hr = S_OK;
CPromptEntry* entry = NULL;
const WCHAR* entryInfo = NULL;
SPDBG_ASSERT (fp);
// S_FALSE indicates no more entries.
while ( hr == S_OK )
{
hr = ReadEntry (fp, &entry);
if ( hr == S_OK && entry )
{
if ( SUCCEEDED(hr) )
{
hr = entry->GetId(&entryInfo);
// prepend the IDSET info to the id
if ( pszIdSet && wcslen(pszIdSet) )
{
WCHAR* pszFullId = NULL;
int iLen = wcslen(pszIdSet) + wcslen(entryInfo) + 1;
pszFullId = new WCHAR[iLen];
if ( pszFullId )
{
wcscpy(pszFullId, pszIdSet);
wcscat(pszFullId, entryInfo);
hr = entry->SetId(pszFullId);
if ( SUCCEEDED(hr) )
{
hr = entry->GetId(&entryInfo);
}
delete [] pszFullId;
}
else
{
hr = E_OUTOFMEMORY;
}
}
if ( SUCCEEDED(hr) )
{
hr = m_pActiveDb->idHash.BuildEntry( entryInfo, entry );
if ( hr == E_INVALIDARG )
{
hr = PEERR_DB_DUPLICATE_ID;
}
entryInfo = NULL;
}
}
entry->Release();
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::SearchDb
//
// Searches the text in the Db entries for items that can be
// concatenated to match the query text. If such a list exists,
// it is put in m_aSearchList.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::SearchDb(const WCHAR* pszQuery, USHORT* unIdCount)
{
SPDBG_FUNC( "CPromptDb::SearchDb" );
HRESULT hr = S_OK;
const CDynStrArray* list = NULL;
USHORT i = 0;
IPromptEntry* pIPE = NULL;
// make sure the search list is clear
for ( i=0; i<m_aSearchList.GetSize(); i++ )
{
m_aSearchList[i].dstr.Clear();
}
m_aSearchList.RemoveAll();
*unIdCount = 0;
if(m_pActiveDb)
{
list = (CDynStrArray*) m_pActiveDb->textHash.Find(pszQuery);
}
if ( !list )
{
hr = E_FAIL;
}
else
{
// If searching the text hash resulted in a list of Ids, save the list.
for ( i=0; i<list->m_aDstr.GetSize(); i++ )
{
m_aSearchList.Add( list->m_aDstr[i].dstr );
}
}
if ( SUCCEEDED(hr) )
{
// go through the list of IDs for this text
// Make sure each id in the list exists; if not, remove it from list.
for ( i=0; i<m_aSearchList.GetSize(); i++)
{
hr = E_FAIL;
// if FindEntry fails, the list size changes and list#'s shift down, so keep checking i
while ( FAILED(hr) && i < m_aSearchList.GetSize() )
{
hr = FindEntry( m_aSearchList[i].dstr, &pIPE );
if ( SUCCEEDED(hr) )
{
pIPE->Release();
}
else
{
m_aSearchList[i].dstr.Clear();
m_aSearchList.RemoveAt( i );
}
}
}
}
if ( m_aSearchList.GetSize() )
{
*unIdCount = (USHORT) m_aSearchList.GetSize();
hr = S_OK;
}
// Don't report failures here - this func is allowed to fail frequently.
//SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::RetrieveSearchItem
//
//
/////////////////////////////////////////////////////// JOEM 5-2000 //
STDMETHODIMP CPromptDb::RetrieveSearchItem(const USHORT unId, const WCHAR** ppszId)
{
SPDBG_FUNC( "CPromptDb::RetrieveSearchItem" );
HRESULT hr = S_OK;
if ( unId >= m_aSearchList.GetSize() )
{
hr = E_INVALIDARG;
}
if ( SUCCEEDED(hr) )
{
*ppszId = m_aSearchList[unId].dstr;
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::GetLogicalName
//
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::GetLogicalName(const WCHAR** ppszLogicalName)
{
SPDBG_FUNC( "CPromptDb::GetLogicalName" );
HRESULT hr = S_OK;
SPDBG_ASSERT( m_pActiveDb );
*ppszLogicalName = m_pActiveDb->pszLogicalName;
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::FindEntry
//
// Locates a Db entry in the hash, and gets an interface for it.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::FindEntry(const WCHAR *id, IPromptEntry** ppIPE)
{
SPDBG_FUNC( "CPromptDb::FindEntry" );
HRESULT hr = S_OK;
CPromptEntry* entry = NULL;
const WCHAR* entryFileName = NULL;
SPDBG_ASSERT(m_pActiveDb);
SPDBG_ASSERT(id);
entry = (CPromptEntry*) m_pActiveDb->idHash.Find(id);
if ( !entry )
{
hr = PEERR_DB_ID_NOT_FOUND;
}
if ( SUCCEEDED(hr) )
{
hr = entry->GetFileName(&entryFileName);
if ( SUCCEEDED(hr) )
{
if ( FileExist( entryFileName ) )
{
hr = entry->QueryInterface(IID_IPromptEntry, (void**)ppIPE);
}
else
{
*ppIPE = NULL;
hr = E_ACCESSDENIED;
}
entryFileName = NULL;
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::NewEntry
//
/////////////////////////////////////////////////////// JOEM 5-2000 //
STDMETHODIMP CPromptDb::NewEntry(IPromptEntry **ppIPE)
{
SPDBG_FUNC( "CPromptDb::NewEntry" );
CPromptEntry* newEntry = new CPromptEntry;
HRESULT hr = S_OK;
if ( newEntry )
{
*ppIPE = newEntry;
}
else
{
hr = E_OUTOFMEMORY;
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::SaveEntry
//
// Adds a Db entry to the Db's hash table
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::SaveEntry(IPromptEntry* pIPE)
{
SPDBG_FUNC( "CPromptDb::SaveEntry" );
HRESULT hr = S_OK;
const WCHAR* entryId = NULL;
double to = 0.0;
double from = 0.0;
if ( SUCCEEDED(hr) )
{
hr = pIPE->GetStart(&from);
}
if ( SUCCEEDED(hr) )
{
hr = pIPE->GetEnd(&to);
}
if ( SUCCEEDED(hr) )
{
USHORT validTime = (from==0.0 && to == -1.0) || (from>=0.0 && to>from);
const WCHAR* entryFile = NULL;
const WCHAR* entryText = NULL;
pIPE->GetId(&entryId);
pIPE->GetText(&entryText);
pIPE->GetFileName(&entryFile);
if ( !entryId || !entryText || !entryFile || !validTime )
{
hr = E_INVALIDARG;
}
}
if ( SUCCEEDED(hr) )
{
hr = m_pActiveDb->idHash.BuildEntry( entryId, pIPE );
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::RemoveEntry
//
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::RemoveEntry(const WCHAR *id)
{
SPDBG_FUNC( "CPromptDb::RemoveEntry" );
HRESULT hr = S_OK;
CPromptEntry* pEntry = NULL;
SPDBG_ASSERT(m_pActiveDb);
SPDBG_ASSERT(id);
if (SUCCEEDED(hr))
{
// CHash will Release it
hr = m_pActiveDb->idHash.DeleteEntry(id);
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::OpenEntryFile
//
// Opens the specified entry.
//
/////////////////////////////////////////////////////// JOEM 5-2000 //
STDMETHODIMP CPromptDb::OpenEntryFile(IPromptEntry *pIPE, WAVEFORMATEX* pWaveFormatEx)
{
SPDBG_FUNC( "CPromptDb::OpenEntryFile" );
HRESULT hr = S_OK;
const WCHAR* entryFile = NULL;
SPDBG_ASSERT (pIPE);
if ( SUCCEEDED(hr) )
{
hr = pIPE->GetFileName(&entryFile);
}
if ( SUCCEEDED(hr) )
{
if ( m_pVapiIO->OpenFile(entryFile, VAPI_IO_READ) != 0 )
{
hr = E_ACCESSDENIED;
}
entryFile = NULL;
}
if ( SUCCEEDED(hr) )
{
if ( m_pVapiIO->Format(NULL, NULL, pWaveFormatEx) != 0 )
{
hr = E_FAIL;
}
}
if ( FAILED(hr) )
{
m_pVapiIO->CloseFile();
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::CloseEntryFile
//
// Retrieves the specified audio samples from the database entry.
//
/////////////////////////////////////////////////////// JOEM 5-2000 //
STDMETHODIMP CPromptDb::CloseEntryFile()
{
m_pVapiIO->CloseFile();
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::GetNextEntry
//
// Retrieves the entry, based on the unique key for the Id Hash.
// To retrieve the first entry, use punId1=0, punId2=0. The Id's
// will be updated with the values that can be used to retrieve the
// next entry.
//
/////////////////////////////////////////////////////// JOEM 6-2000 //
STDMETHODIMP CPromptDb::GetNextEntry(USHORT* punId1, USHORT* punId2, IPromptEntry** ppIPE)
{
SPDBG_FUNC( "CPromptDb::GetNextEntry" );
HRESULT hr = S_OK;
WCHAR* pszNextKey = NULL;
SPDBG_ASSERT(punId1);
SPDBG_ASSERT(punId2);
hr = m_pActiveDb->idHash.NextKey( punId1, punId2, &pszNextKey );
if ( !pszNextKey )
{
hr = E_FAIL;
}
if ( SUCCEEDED(hr) )
{
hr = FindEntry(pszNextKey, ppIPE);
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::SetOutputFormat
//
// Sets the format in which samples are returned to the calling app.
//
/////////////////////////////////////////////////////// JOEM 5-2000 //
STDMETHODIMP CPromptDb::SetOutputFormat(const GUID *pOutputFormatId, const WAVEFORMATEX *pOutputFormat)
{
SPDBG_FUNC( "CPromptDb::SetOutputFormat" );
HRESULT hr = S_OK;
if ( pOutputFormatId && *pOutputFormatId == SPDFID_Text )
{
m_pOutputFormatId = pOutputFormatId;
if ( m_pOutputFormat )
{
delete m_pOutputFormat;
m_pOutputFormat = NULL;
}
}
else if ( pOutputFormat )
{
// Do we need to change the format?
if ( !m_pOutputFormat ||
m_pOutputFormat->wFormatTag != pOutputFormat->wFormatTag ||
m_pOutputFormat->nChannels != pOutputFormat->nChannels ||
m_pOutputFormat->nSamplesPerSec != pOutputFormat->nSamplesPerSec ||
m_pOutputFormat->nAvgBytesPerSec != pOutputFormat->nAvgBytesPerSec ||
m_pOutputFormat->nBlockAlign != pOutputFormat->nBlockAlign ||
m_pOutputFormat->wBitsPerSample != pOutputFormat->wBitsPerSample ||
m_pOutputFormat->cbSize != pOutputFormat->cbSize
)
{
// free the current waveformatex
if ( m_pOutputFormat )
{
free(m_pOutputFormat);
m_pOutputFormat = NULL;
}
// this needs to copy the output format, not just point to it,
// because engine will pass in const pointer.
m_pOutputFormat = (WAVEFORMATEX*) malloc( sizeof(WAVEFORMATEX) + pOutputFormat->cbSize );
if ( !m_pOutputFormat )
{
hr = E_OUTOFMEMORY;
}
else
{
m_pOutputFormatId = pOutputFormatId;
memcpy(m_pOutputFormat, pOutputFormat, sizeof(WAVEFORMATEX) + pOutputFormat->cbSize );
}
if ( SUCCEEDED(hr) )
{
m_converter.SetOutputFormat(m_pOutputFormat);
}
if ( SUCCEEDED(hr) && m_pTsm )
{
m_pTsm->SetSamplingFrequency(m_pOutputFormat->nSamplesPerSec);
}
}
}
else
{
hr = E_INVALIDARG;
}
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::SetEntryGain
//
// Sets the gain factor (from registry) for Prompt entries.
//
/////////////////////////////////////////////////////// JOEM 6-2000 //
STDMETHODIMP CPromptDb::SetEntryGain(const double dEntryGain)
{
SPDBG_FUNC( "CPromptDb::SetEntryGain" );
m_flEntryGain = (float)dEntryGain;
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::SetXMLVolume
//
// Adjust the volume from XML tag.
//
/////////////////////////////////////////////////////// JOEM 6-2000 //
STDMETHODIMP CPromptDb::SetXMLVolume(const ULONG ulXMLVol)
{
SPDBG_FUNC( "CPromptDb::SetXMLVolume" );
SPDBG_ASSERT(ulXMLVol <=100);
m_flXMLVolume = ((double) ulXMLVol) / 100;
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::SetXMLRate
//
// Adjust the rate from XML tag.
//
/////////////////////////////////////////////////////// JOEM 11-2000 //
STDMETHODIMP CPromptDb::SetXMLRate(const long lXMLRate)
{
SPDBG_FUNC( "CPromptDb::SetXMLRate" );
ComputeRateAdj(lXMLRate, &m_flXMLRateAdj);
return S_OK;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::SendEntrySamples
//
// Retrieves the specified audio samples from the database entry,
// applies rate change, converts them, applies gain/vol, and sends
// them to output site.
//
/////////////////////////////////////////////////////// JOEM 5-2000 //
STDMETHODIMP CPromptDb::SendEntrySamples(IPromptEntry* pIPE, ISpTTSEngineSite* pOutputSite, ULONG ulTextOffset, ULONG ulTextLen)
{
HRESULT hr = S_OK;
double entryTo = 0.0;
double entryFrom = 0.0;
ULONG pcbWritten = 0;
void* pvSamples = NULL;
void* pvNewSamples = NULL;
int iNumSamples = 0;
int iNumNewSamples = 0;
USHORT unVol = 1; // Full vol unless app changes it
double dVol = 1.0; // Full vol unless app changes it
long lRateAdj = 1; // Regular rate unless app changes it
float flRateAdj = 1.0; // Regular rate unless app changes it
double dBuff = 0.0;
USHORT i = 0;
bool fConvert = false;
WAVEFORMATEX wavFormat;
SPDBG_ASSERT(pIPE);
SPDBG_ASSERT(pOutputSite);
hr = OpenEntryFile(pIPE, &wavFormat);
if ( SUCCEEDED(hr) )
{
// Does the current prompt format need converting?
if ( m_pOutputFormat )
{
if (wavFormat.wFormatTag != m_pOutputFormat->wFormatTag ||
wavFormat.nAvgBytesPerSec != m_pOutputFormat->nAvgBytesPerSec ||
wavFormat.nBlockAlign != m_pOutputFormat->nBlockAlign ||
wavFormat.nChannels != m_pOutputFormat->nChannels ||
wavFormat.nSamplesPerSec != m_pOutputFormat->nSamplesPerSec ||
wavFormat.wBitsPerSample != m_pOutputFormat->wBitsPerSample )
{
fConvert = true;
m_converter.SetInputFormat(&wavFormat);
}
}
// get the start and end points, and clean them up if necessary.
if ( SUCCEEDED(hr) )
{
hr = pIPE->GetStart(&entryFrom);
if ( SUCCEEDED(hr) )
{
if ( entryFrom < 0.0 )
{
entryFrom = 0.0;
}
hr = pIPE->GetEnd(&entryTo);
}
if ( SUCCEEDED(hr) )
{
long lDataSize = 0;
double dEnd = 0.0;
// make sure we're not trying to read past end of file
m_pVapiIO->GetDataSize(&lDataSize);
dEnd = ((double)lDataSize)/wavFormat.nBlockAlign/wavFormat.nSamplesPerSec;
if ( entryTo > dEnd )
{
entryTo = dEnd;
}
if ( entryTo != -1.0 && entryFrom >= entryTo )
{
entryFrom = entryTo;
}
}
}
if ( SUCCEEDED(hr) )
{
// read the samples
if ( SUCCEEDED(hr) )
{
if ( m_pVapiIO->ReadSamples( entryFrom, entryTo, &pvSamples, &iNumSamples, 1) != 0 )
{
hr = E_FAIL;
}
if ( iNumSamples == 0 )
{
hr = E_FAIL;
}
}
// convert to desired output format
if ( SUCCEEDED(hr) && fConvert && pvSamples )
{
hr = m_converter.ConvertSamples(pvSamples, iNumSamples, (void**) &pvNewSamples, &iNumNewSamples);
if ( SUCCEEDED(hr) )
{
delete [] pvSamples;
pvSamples = pvNewSamples;
iNumSamples = iNumNewSamples;
pvNewSamples = NULL;
iNumNewSamples = 0;
}
}
// do rate change. Must be format-converted already!
if ( SUCCEEDED(hr) && m_pTsm )
{
// Don't GetActions - the rate flag is kept on since TTS eng needs it too.
hr = pOutputSite->GetRate( &lRateAdj );
if ( SUCCEEDED(hr) )
{
ComputeRateAdj(lRateAdj, &flRateAdj);
flRateAdj *= m_flXMLRateAdj;
m_pTsm->SetScaleFactor( (double) flRateAdj );
if ( flRateAdj != 1.0 && pvSamples )
{
// can't adjust rate for chunks smaller than the frame length
if ( iNumSamples > m_pTsm->GetFrameLen() )
{
hr = m_pTsm->AdjustTimeScale( pvSamples, iNumSamples, &pvNewSamples, &iNumNewSamples, m_pOutputFormat );
if ( SUCCEEDED(hr) )
{
delete [] pvSamples;
pvSamples = pvNewSamples;
iNumSamples = iNumNewSamples;
pvNewSamples = NULL;
iNumNewSamples = 0;
}
}
}
}
}
// check for volume change
if ( SUCCEEDED(hr) )
{
// Prompt Eng doesn't GetActions - the Vol flag is kept on since TTS eng needs it.
hr = pOutputSite->GetVolume( &unVol );
}
// Multiply the three volume sources
if ( SUCCEEDED(hr) )
{
dVol = ((double) unVol) / 100; // make dVol fractional
dVol *= m_flEntryGain; // gain from registry
dVol *= m_flXMLVolume; // gain from XML
if ( dVol != 1.0 )
{
hr = ApplyGain( pvSamples, &pvNewSamples, iNumSamples, dVol );
// Did ApplyGain need to create a new buffer?
if ( pvNewSamples )
{
// send the volume modified buff instead of the original
delete [] pvSamples;
pvSamples = pvNewSamples;
pvNewSamples = NULL;
}
}
}
// EVENTS:
// Send a private engine event at beginning of each prompt
if ( SUCCEEDED(hr) )
{
hr = SendEvent(SPEI_TTS_PRIVATE, pOutputSite, 0, ulTextOffset, ulTextLen);
}
// Mid-prompt pausing requires word boundaries. We don't know word-boundaries within
// prompts, so approximate them by sending a word boundary event for every half-second
// of audio.
if ( SUCCEEDED(hr) )
{
ULONG ulOffset = 0;
int iHalfSecs = iNumSamples / m_pOutputFormat->nSamplesPerSec * 2;
for ( i = 0; i <= iHalfSecs; i++ )
{
ulOffset = i * (m_pOutputFormat->nSamplesPerSec / 2) * m_pOutputFormat->nBlockAlign;
SendEvent(SPEI_WORD_BOUNDARY, pOutputSite, ulOffset, ulTextOffset, ulTextLen);
}
}
// write samples to output site
if ( SUCCEEDED(hr) )
{
hr = ((CLocalTTSEngineSite*)(pOutputSite))->Write(pvSamples, iNumSamples * m_pOutputFormat->nBlockAlign, &pcbWritten);
if ( SUCCEEDED(hr) )
{
((CLocalTTSEngineSite*)(pOutputSite))->UpdateBytesWritten();
}
delete [] pvSamples;
pvSamples = NULL;
iNumSamples = 0;
pcbWritten = 0;
}
} // if ( SUCCEEDED(hr) )
hr = CloseEntryFile();
} // if ( SUCCEEDED(hr)
if ( pvSamples )
{
delete[]pvSamples;
pvSamples = NULL;
iNumSamples = 0;
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
//
// All of the following functions are non-interface helpers.
//
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// CPromptDb::ComputeRateAdj
//
// Computes the rate multiplier.
//
/////////////////////////////////////////////////////// JOEM 11-2000 //
void CPromptDb::ComputeRateAdj(const long lRate, float* flRate)
{
SPDBG_FUNC( "CPromptDb::ComputeRateAdj" );
SPDBG_ASSERT(flRate);
if ( lRate < 0 )
{
if ( lRate < MIN_RATE )
{
*flRate = 1.0 / g_dRateScale[0 - MIN_RATE];
}
else
{
*flRate = 1.0 / g_dRateScale[0 - lRate];
}
}
else
{
if ( lRate > MAX_RATE )
{
*flRate = g_dRateScale[MAX_RATE];
}
else
{
*flRate = g_dRateScale[lRate];
}
}
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::SendEvent
//
// Sends the specified engine event to SAPI.
//
////////////////////////////////////////////////////// JOEM 11-2000 //
STDMETHODIMP CPromptDb::SendEvent(const SPEVENTENUM eventName, ISpTTSEngineSite* pOutputSite,
const ULONG ulAudioOffset, const ULONG ulTextOffset, const ULONG ulTextLen)
{
SPDBG_ASSERT(pOutputSite);
SPEVENT event;
event.eEventId = eventName;
event.elParamType = SPET_LPARAM_IS_UNDEFINED;
event.ulStreamNum = 0;
event.ullAudioStreamOffset = ulAudioOffset; //LocalEngineSite will add prev. bytes
event.lParam = ulTextOffset;
event.wParam = ulTextLen;
return pOutputSite->AddEvents( &event, 1 );
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::ApplyGain
//
// Volume adjustment.
//
////////////////////////////////////////////////////// JOEM 01-2001 //
STDMETHODIMP CPromptDb::ApplyGain(const void* pvInBuff, void** ppvOutBuff, const int iNumSamples, double dGain)
{
SPDBG_FUNC( "CPromptDb::ApplyGain" );
HRESULT hr = S_OK;
int i = 0;
SPDBG_ASSERT(pvInBuff);
SPDBG_ASSERT(iNumSamples);
// Apply Volume
if ( SUCCEEDED(hr) && dGain != 1.0 )
{
// NOTE THAT REGISTRY GAIN VALUE MAY BE GREATER THAN 1.
// Make sure it is in bounds of SHRT_MAX, since we'll be multiplying it times samples
if ( dGain > SHRT_MAX )
{
dGain = SHRT_MAX;
}
else if ( dGain < 0 ) // gain should never be < 0.
{
dGain = 1.0;
}
long lGain = ( dGain * (1 << g_iBase) );
if ( m_pOutputFormat->wFormatTag == WAVE_FORMAT_ALAW || m_pOutputFormat->wFormatTag == WAVE_FORMAT_MULAW )
{
short* pnBuff = NULL;
// need to convert samples
int iOriginalFormatType = VapiIO::TypeOf (m_pOutputFormat);
if ( iOriginalFormatType == -1 )
{
hr = E_FAIL;
}
// Allocate the intermediate buffer
if ( SUCCEEDED(hr) )
{
pnBuff = new short[iNumSamples];
if ( !pnBuff )
{
hr = E_OUTOFMEMORY;
}
}
// Allocate the final (out) buffer
if ( SUCCEEDED(hr) )
{
*ppvOutBuff = new char[iNumSamples * VapiIO::SizeOf(iOriginalFormatType)];
if ( !*ppvOutBuff )
{
hr = E_OUTOFMEMORY;
}
}
// Convert to something we can use
if ( SUCCEEDED(hr) )
{
if ( 0 == VapiIO::DataFormatConversion ((char *)pvInBuff, iOriginalFormatType, (char*)pnBuff, VAPI_PCM16, iNumSamples) )
{
hr = E_FAIL;
}
}
// Apply gain
if ( SUCCEEDED(hr) )
{
double dSample = 0;
for ( i=0; i<iNumSamples; i++ )
{
dSample = pnBuff[i] * lGain;
if ( dSample > LONG_MAX )
{
pnBuff[i] = (short) (LONG_MAX >> g_iBase);
}
else if ( dSample < LONG_MIN )
{
pnBuff[i] = (short) (LONG_MIN >> g_iBase);
}
else
{
pnBuff[i] = (short) ( ( pnBuff[i] * lGain ) >> g_iBase );
}
}
}
// convert it back (from intermediate buff to final out buff)
if ( SUCCEEDED(hr) )
{
if ( 0 == VapiIO::DataFormatConversion ((char *)pnBuff, VAPI_PCM16, (char*)*ppvOutBuff, iOriginalFormatType, iNumSamples) )
{
hr = E_FAIL;
}
}
if ( pnBuff )
{
delete [] pnBuff;
pnBuff = NULL;
}
}
else if ( m_pOutputFormat->wFormatTag == WAVE_FORMAT_PCM )
{
double dSample = 0;
// no converting necessary
switch ( m_pOutputFormat->nBlockAlign )
{
case 1:
for ( i=0; i<iNumSamples; i++ )
{
for ( i=0; i<iNumSamples; i++ )
{
dSample = ((char*)pvInBuff)[i] * lGain;
if ( dSample > LONG_MAX )
{
((char*)pvInBuff)[i] = (char) (LONG_MAX >> g_iBase);
}
else if ( dSample < LONG_MIN )
{
((char*)pvInBuff)[i] = (char) (LONG_MIN >> g_iBase);
}
else
{
((char*)pvInBuff)[i] = (char) ( ( ((char*)pvInBuff)[i] * lGain ) >> g_iBase );
}
}
}
break;
case 2:
for ( i=0; i<iNumSamples; i++ )
{
dSample = ((short*)pvInBuff)[i] * lGain;
if ( dSample > LONG_MAX )
{
((short*)pvInBuff)[i] = (short) (LONG_MAX >> g_iBase);
}
else if ( dSample < LONG_MIN )
{
((short*)pvInBuff)[i] = (short) (LONG_MIN >> g_iBase);
}
else
{
((short*)pvInBuff)[i] = (short) ( ( ((short*)pvInBuff)[i] * lGain ) >> g_iBase );
}
}
break;
default:
hr = E_FAIL;
}
}
else
{
hr = E_FAIL;
}
}
if ( FAILED(hr) )
{
if ( *ppvOutBuff )
{
delete [] *ppvOutBuff;
*ppvOutBuff = NULL;
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::ReadEntry
//
// Reads a Db entry from the Db file.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CPromptDb::ReadEntry(FILE* fp, CPromptEntry** ppEntry)
{
SPDBG_FUNC( "CPromptDb::ReadEntry" );
HRESULT hr = S_OK;
WCHAR* entryInfo = NULL;
WCHAR* ptr = NULL;
WCHAR* tmpTag = NULL;
WCHAR* start = NULL;
SHORT state = 0;
double dEntryInfo = 0.0;
WCHAR line[1024];
WCHAR fullPath[_MAX_PATH+1] = L"";
WCHAR dir[_MAX_DIR] = L"";
bool fId = false;
bool fFile = false;
bool fText = false;
bool fOriginal = false;
bool fFrom = false;
bool fTo = false;
bool fStart = false;
bool fEnd = false;
bool fLeft = false;
bool fRight = false;
SPDBG_ASSERT (fp);
*ppEntry = new CPromptEntry;
if (!*ppEntry)
{
hr = E_OUTOFMEMORY;
}
while ( ( state != -1 ) && SUCCEEDED(hr) )
{
if ( !fgetws(line, 1024, fp) )
{
if (state == 0)
{
(*ppEntry)->Release();
*ppEntry = NULL;
hr = S_FALSE;
break; /* No more entries to read */
}
else
{
hr = PEERR_DB_BAD_FORMAT; /* EOF in the middle of reading an entry*/
}
}
if ( SUCCEEDED(hr) )
{
// Strip off the newline character
if (line[wcslen(line)-1] == L'\n')
{
line[wcslen(line)-1] = L'\0';
}
// Line ends when a comment marker is found
if ( (ptr = wcschr (line, L'#')) != NULL ) {
*ptr = L'\0';
}
ptr = line;
WSkipWhiteSpace (ptr);
}
if ( SUCCEEDED(hr) )
{
while ( SUCCEEDED(hr) && *ptr )
{
switch (state)
{
case 0:
// Search for the starting tag
// Discard everything up to the starting tag
if ( wcsncmp( ptr, ENTRY_TAG, sizeof(ENTRY_TAG)/sizeof(ENTRY_TAG[0]) - 1 ) != 0)
{
hr = PEERR_DB_BAD_FORMAT;
}
if ( SUCCEEDED (hr) )
{
ptr += wcslen (ENTRY_TAG);
state = 1;
}
break;
case 1:
WSkipWhiteSpace (ptr);
if (*ptr)
{
if ( wcsncmp(ptr, ENTRY_START, sizeof(ENTRY_START)/sizeof(ENTRY_START[0]) - 1 ) ==0 )
{
ptr += wcslen (ENTRY_START);
state = 2;
}
else
{
hr = PEERR_DB_BAD_FORMAT;
}
}
break;
case 2:
// Only one item per line
if ( (start = wcsstr(ptr, ENTRY_END)) != NULL)
{
// If we find the entry_end marker, this is the last
// line. Remember, this line could have an item also
*start = L'\0';
state = -1;
}
WSkipWhiteSpace (ptr);
if (!*ptr)
{
break;
}
// Must be one of this elements of the entry,
// otherwise, error
hr = ExtractString (ptr, ID_TAG, &entryInfo);
if ( SUCCEEDED (hr) )
{
if ( fId )
{
hr = PEERR_DB_BAD_FORMAT;
//SPDBG_ASSERT (! "Duplicate ID field in database entry.");
}
if ( SUCCEEDED(hr) )
{
hr = (*ppEntry)->SetId(entryInfo);
}
fId = true;
ptr = L"";
free(entryInfo);
entryInfo = NULL;
break;
}
hr = ExtractString (ptr, TEXT_TAG, &entryInfo);
if ( SUCCEEDED (hr) )
{
if ( fText )
{
hr = PEERR_DB_BAD_FORMAT;
//SPDBG_ASSERT (! "Duplicate TEXT field in database entry.");
}
if ( SUCCEEDED(hr) )
{
hr = (*ppEntry)->SetText(entryInfo);
}
fText = true;
ptr = L"";
free(entryInfo);
entryInfo = NULL;
break;
}
hr = ExtractString (ptr, ORIGINAL_TAG, &entryInfo);
if ( SUCCEEDED (hr) )
{
if ( fOriginal )
{
hr = PEERR_DB_BAD_FORMAT;
//SPDBG_ASSERT (! "Duplicate TEXT field in database entry.");
}
if ( SUCCEEDED(hr) )
{
hr = (*ppEntry)->SetOriginalText(entryInfo);
}
fOriginal = true;
ptr = L"";
free(entryInfo);
entryInfo = NULL;
break;
}
hr = ExtractString (ptr, FILE_TAG, &entryInfo);
if ( SUCCEEDED (hr) )
{
if ( fFile )
{
hr = PEERR_DB_BAD_FORMAT;
//SPDBG_ASSERT (! "Duplicate FILE NAME field in database entry.");
}
if ( SUCCEEDED(hr) )
{
if ( entryInfo )
{
// Is it a full path? (i.e, does it start with a "\" or is 2nd char a colon)
if ( wcslen(entryInfo) >= 2 && ( entryInfo[0] == L'\\' || entryInfo[1] == L':' ) )
{
hr = (*ppEntry)->SetFileName(entryInfo);
}
else // must be a relative path
{
// Construct the full path to the entry
_wsplitpath(m_pActiveDb->pszPathName, fullPath, dir, NULL, NULL);
wcscat(fullPath, dir);
wcscat(fullPath, entryInfo);
// Don't check this path here. We don't want Db loading to fail
// if a wav doesn't exist -- load the db anyway, and later when
// searching db, we check the paths and resort to TTS if path is invalid.
hr = (*ppEntry)->SetFileName(fullPath);
}
}
}
fFile = true;
ptr = L"";
free(entryInfo);
entryInfo = NULL;
break;
}
hr = ExtractString (ptr, TAG_TAG, &entryInfo);
if ( SUCCEEDED (hr) )
{
hr = (*ppEntry)->AddTag(entryInfo);
ptr = L"";
free(entryInfo);
entryInfo = NULL;
break;
}
hr = ExtractString (ptr, START_PHONE_TAG, &entryInfo);
if ( SUCCEEDED (hr) )
{
if ( fStart )
{
hr = PEERR_DB_BAD_FORMAT;
//SPDBG_ASSERT (! "Duplicate START PHONE field in database entry.");
}
if ( SUCCEEDED(hr) )
{
hr = (*ppEntry)->SetStartPhone(entryInfo);
}
fStart = true;
ptr = L"";
free(entryInfo);
entryInfo = NULL;
break;
}
hr = ExtractString (ptr, END_PHONE_TAG, &entryInfo);
if ( SUCCEEDED (hr) )
{
if ( fEnd )
{
hr = PEERR_DB_BAD_FORMAT;
//SPDBG_ASSERT (! "Duplicate END PHONE field in database entry.");
}
if ( SUCCEEDED(hr) )
{
hr = (*ppEntry)->SetEndPhone(entryInfo);
}
fEnd = true;
ptr = L"";
free(entryInfo);
entryInfo = NULL;
break;
}
hr = ExtractString (ptr, RIGHT_CONTEXT_TAG, &entryInfo);
if ( SUCCEEDED (hr) )
{
if ( fRight )
{
hr = PEERR_DB_BAD_FORMAT;
//SPDBG_ASSERT (! "Duplicate RIGHT CONTEXT field in database entry.");
}
if ( SUCCEEDED(hr) )
{
hr = (*ppEntry)->SetRightContext(entryInfo);
}
fRight = true;
ptr = L"";
free(entryInfo);
entryInfo = NULL;
break;
}
hr = ExtractString (ptr, LEFT_CONTEXT_TAG, &entryInfo);
if ( SUCCEEDED (hr) )
{
if ( fLeft )
{
hr = PEERR_DB_BAD_FORMAT;
//SPDBG_ASSERT (! "Duplicate LEFT CONTEXT field in database entry.");
}
if ( SUCCEEDED(hr) )
{
hr = (*ppEntry)->SetLeftContext(entryInfo);
}
fLeft = true;
ptr = L"";
free(entryInfo);
entryInfo = NULL;
break;
}
hr = ExtractDouble (ptr, FROM_TAG, &dEntryInfo);
if ( SUCCEEDED (hr) )
{
if ( fFrom )
{
hr = PEERR_DB_BAD_FORMAT;
//SPDBG_ASSERT (! "Duplicate FROM field in database entry.");
}
if ( SUCCEEDED(hr) )
{
hr = (*ppEntry)->SetStart(dEntryInfo);
}
fFrom = true;
ptr = L"";
break;
}
hr = ExtractDouble (ptr, TO_TAG, &dEntryInfo);
if ( SUCCEEDED (hr) )
{
if ( fTo )
{
hr = PEERR_DB_BAD_FORMAT;
//SPDBG_ASSERT (! "Duplicate TO field in database entry.");
}
if ( SUCCEEDED(hr) )
{
hr = (*ppEntry)->SetEnd(dEntryInfo);
}
fTo = true;
ptr = L"";
break;
}
hr = PEERR_DB_BAD_FORMAT;
} // switch (state)
} // while (*ptr)
}
} // while ( SUCCEEDED(hr) && state != -1)
// THESE ARE THE MANDATORY FIELDS FOR THE DATABASE ENTRY
if ( *ppEntry && SUCCEEDED(hr) )
{
if ( !fId )
{
//SPDBG_ASSERT (! "Missing ID field in database entry.");
hr = PEERR_DB_BAD_FORMAT;
}
if ( !fFile )
{
//SPDBG_ASSERT (! "Missing FILE field in database entry.");
hr = PEERR_DB_BAD_FORMAT;
}
if ( !fText )
{
//SPDBG_ASSERT (! "Missing TEXT field in database entry.");
hr = PEERR_DB_BAD_FORMAT;
}
if ( !fFrom )
{
//SPDBG_ASSERT (! "Missing FROM field in database entry.");
hr = PEERR_DB_BAD_FORMAT;
}
if ( !fTo )
{
//SPDBG_ASSERT (! "Missing TO field in database entry.");
hr = PEERR_DB_BAD_FORMAT;
}
}
// From is less than To?
if ( *ppEntry && SUCCEEDED(hr) )
{
double from = 0.0;
double to = 0.0;
hr = (*ppEntry)->GetStart(&from);
if ( SUCCEEDED(hr) )
{
hr = (*ppEntry)->GetEnd(&to);
}
if ( SUCCEEDED(hr) )
{
if ( to <= from )
{
// a value of -1.0 is ok for "to" -- it means play to end of file.
if ( to != -1.0 )
{
hr = PEERR_DB_BAD_FORMAT;
}
}
}
}
if ( FAILED(hr) && *ppEntry )
{
(*ppEntry)->Release();
*ppEntry = NULL;
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::WriteEntry
//
// Writes a formated entry to a Db file.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CPromptDb::WriteEntry(FILE *fp, IPromptEntry *pIPE)
{
SPDBG_FUNC( "CPromptDb::WriteEntry" );
HRESULT hr = S_OK;
const WCHAR* entryText = NULL;
SPDBG_ASSERT (fp);
SPDBG_ASSERT (pIPE);
fwprintf (fp, L"%s %s\n", ENTRY_TAG, ENTRY_START);
if ( SUCCEEDED(hr) )
{
hr = pIPE->GetId(&entryText);
if ( SUCCEEDED(hr) )
{
fwprintf (fp, L"\t%s %s\n", ID_TAG, entryText);
entryText = NULL;
}
}
if ( SUCCEEDED(hr) )
{
hr = pIPE->GetText(&entryText);
if ( SUCCEEDED(hr) )
{
fwprintf (fp, L"\t%s %s\n", TEXT_TAG, entryText);
entryText = NULL;
}
}
if ( SUCCEEDED(hr) )
{
hr = pIPE->GetOriginalText(&entryText);
if ( SUCCEEDED(hr) )
{
fwprintf (fp, L"\t%s %s\n", ORIGINAL_TAG, entryText);
entryText = NULL;
}
}
if ( SUCCEEDED(hr) )
{
USHORT entryTagCount = 0;
hr = pIPE->CountTags(&entryTagCount);
if ( SUCCEEDED(hr) && entryTagCount )
{
for ( USHORT i=0; i < entryTagCount; i++)
{
hr = pIPE->GetTag(&entryText, i);
if ( SUCCEEDED(hr) )
{
fwprintf( fp, L"\t%s %s\n", TAG_TAG, entryText );
entryText = NULL;
}
}
}
}
if ( SUCCEEDED(hr) )
{
hr = pIPE->GetFileName(&entryText);
if ( SUCCEEDED(hr) )
{
fwprintf (fp, L"\t%s %s\n", FILE_TAG, entryText);
entryText = NULL;
}
}
if ( SUCCEEDED(hr) )
{
double start = 0.0;
hr = pIPE->GetStart(&start);
if ( SUCCEEDED(hr) )
{
fwprintf( fp, L"\t%s %f\n", FROM_TAG, start);
}
}
if ( SUCCEEDED(hr) )
{
double end = 0.0;
hr = pIPE->GetEnd(&end);
if ( SUCCEEDED(hr) )
{
fwprintf( fp, L"\t%s %f\n", TO_TAG, end);
}
}
fwprintf (fp, L"%s\n\n", ENTRY_END);
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::DuplicateEntry
//
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
CPromptEntry* CPromptDb::DuplicateEntry(const CPromptEntry *oldEntry)
{
SPDBG_FUNC( "CPromptDb::DuplicateEntry" );
CPromptEntry* newEntry = new CPromptEntry(*oldEntry);
return newEntry;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::ExtractString
//
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CPromptDb::ExtractString(WCHAR *line, const WCHAR *tag, WCHAR **value)
{
SPDBG_FUNC( "CPromptDb::ExtractString" );
HRESULT hr = S_OK;
int tagLen = 0;
int entryLen = 0;
tagLen = (USHORT) wcslen (tag);
if (wcsncmp(line, tag, tagLen)==0)
{
line += tagLen;
WSkipWhiteSpace (line);
for (entryLen = wcslen(line) -1; entryLen>=0 && iswspace(line[entryLen]); entryLen--)
{
line[entryLen] = L'\0';
}
// Control characters are not allowed in Db
if ( FindUnicodeControlChar(line) )
{
hr = E_UNEXPECTED;
}
if ( SUCCEEDED(hr) && !wcslen(line) )
{
hr = E_INVALIDARG;
}
if ( SUCCEEDED(hr) )
{
if ((*value = _wcsdup(line)) == NULL)
{
hr = E_OUTOFMEMORY;
}
}
}
else
{
hr = E_FAIL;
}
// Don't report failures here - this func is allowed to fail frequently.
//SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::ExtractDouble
//
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CPromptDb::ExtractDouble(WCHAR *line, const WCHAR *tag, double *value)
{
SPDBG_FUNC( "CPromptDb::ExtractDouble" );
HRESULT hr = S_OK;
int tagLen = 0;
int entryLen = 0;
WCHAR* psz = NULL;
*value = 0.0;
tagLen = (USHORT) wcslen (tag);
if (wcsncmp(line, tag, tagLen)==0)
{
line += tagLen;
WSkipWhiteSpace (line);
for (entryLen = wcslen(line) -1; entryLen>=0 && iswspace(line[entryLen]); entryLen--)
{
line[entryLen] = L'\0';
}
// Control characters are not allowed in Db
if ( FindUnicodeControlChar(line) )
{
hr = E_UNEXPECTED;
}
if ( SUCCEEDED(hr) && !wcslen(line) )
{
hr = E_INVALIDARG;
}
// make sure it's a number
if ( SUCCEEDED(hr) )
{
psz = line;
while ( SUCCEEDED(hr) && psz[0] )
{
if ( iswdigit(psz[0]) || psz[0] == L'.' || psz[0] == L'-' )
{
psz++;
}
else
{
hr = E_INVALIDARG;
}
}
}
if ( SUCCEEDED(hr) )
{
*value = wcstod( line, NULL );
}
if ( SUCCEEDED(hr) && *value < 0 && *value != -1.0 )
{
hr = E_INVALIDARG;
}
}
else
{
hr = E_FAIL;
}
// Don't report failures here - this func is allowed to fail frequently.
//SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::TempName
//
// Creates a temporary file name for the active db.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CPromptDb::TempName()
{
SPDBG_FUNC( "CPromptDb::TempName" );
HRESULT hr = S_OK;
WCHAR nameStr[256];
SPDBG_ASSERT( m_pActiveDb );
SPDBG_ASSERT( m_pActiveDb->pszPathName );
wcscat( wcscpy( nameStr, m_pActiveDb->pszPathName ), L".tmp" );
if ( m_pActiveDb->pszTempName && (wcscmp( m_pActiveDb->pszTempName, nameStr ) != 0) )
{
free (m_pActiveDb->pszTempName);
m_pActiveDb->pszTempName = NULL;
}
if ( !m_pActiveDb->pszTempName )
{
if ( ( m_pActiveDb->pszTempName = _wcsdup(nameStr) ) == NULL)
{
hr = E_OUTOFMEMORY;
}
}
SPDBG_REPORT_ON_FAIL( hr );
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::IndexTextHash
//
// Makes a text hash. Key: text
// Value: list of IDs for that text
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
HRESULT CPromptDb::IndexTextHash()
{
SPDBG_FUNC( "CPromptDb::IndexTextHash" );
HRESULT hr = S_OK;
WCHAR* id = NULL;
const WCHAR* entryInfo = NULL;
USHORT idx1 = 0;
USHORT idx2 = 0;
USHORT i = 0;
USHORT found = 0;
CPromptEntry* entry = NULL;
CDynStrArray* candList = NULL;
while ( SUCCEEDED(hr) )
{
m_pActiveDb->idHash.NextKey (&idx1, &idx2, &id);
if ( !id )
{
// this was the last one
break;
}
if ( SUCCEEDED(hr) )
{
entry = (CPromptEntry*) m_pActiveDb->idHash.Find(id);
if ( !entry )
{
hr = E_UNEXPECTED;
}
}
if ( SUCCEEDED(hr) )
{
hr = entry->GetText(&entryInfo);
if ( SUCCEEDED(hr) )
{
// Is there already a list of entries for this text? If not, create one.
if ( (candList = (CDynStrArray*) m_pActiveDb->textHash.Find(entryInfo)) == NULL )
{
candList = new CDynStrArray;
if (!candList)
{
hr = E_OUTOFMEMORY;
}
if ( SUCCEEDED(hr) )
{
hr = m_pActiveDb->textHash.BuildEntry(entryInfo, candList);
}
}
}
entryInfo = NULL;
if ( SUCCEEDED(hr) )
{
// Search the list, check that the entry doesn't exist
found = 0;
hr = entry->GetId(&entryInfo);
if ( SUCCEEDED(hr) )
{
for ( i = 0; i < candList->m_aDstr.GetSize(); i++ )
{
if ( wcscmp(candList->m_aDstr[i].dstr, entryInfo) == 0 )
{
found = 1;
break;
}
}
if ( !found )
{
// Add item to the list, the list is already in the hash table
candList->m_aDstr.Add(entryInfo);
}
entryInfo = NULL;
}
}
entry = NULL;
} // if ( SUCCEEDED(hr) )
} // while
if ( FAILED(hr) )
{
if ( candList )
{
delete candList;
candList = NULL;
}
}
return hr;
}
//////////////////////////////////////////////////////////////////////
// CPromptDb::GetPromptFormat
//
// Gets the format of the first prompt file, used for setting the
// output format. Note, however, that the application can change the
// format, and that prompt files do not need to be all in the same
// format.
//
/////////////////////////////////////////////////////// JOEM 3-2000 //
STDMETHODIMP CPromptDb::GetPromptFormat (WAVEFORMATEX **ppwf)
{
HRESULT hr = E_FAIL;
USHORT unId1 = 0;
USHORT unId2 = 0;
WCHAR *pszId = NULL;
IPromptEntry* pIPE = NULL;
while ( FAILED(hr) )
{
hr = GetNextEntry(&unId1, &unId2, &pIPE);
}
if ( hr == S_OK )
{
WAVEFORMATEX* newWF = (WAVEFORMATEX*)::CoTaskMemAlloc( sizeof(WAVEFORMATEX) );
if ( newWF )
{
hr = pIPE->GetFormat( &newWF );
newWF->cbSize = 0;
}
else
{
hr = E_OUTOFMEMORY;
}
if ( SUCCEEDED(hr) )
{
*ppwf = newWF;
}
}
return hr;
}