diff -r cad71a31b7fc -r e36f3802f733 srsf/nssvasapi/nssvascore/src/nssvascspeechitemtrainer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/srsf/nssvasapi/nssvascore/src/nssvascspeechitemtrainer.cpp Wed Sep 01 12:29:17 2010 +0100 @@ -0,0 +1,1965 @@ +/* +* Copyright (c) 2004-2006 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: The CNssSpeechItemTrainer provides methods to train, save +* and delete CNssSpeechItem objects in large quantities. +* +*/ + + +/* Here is a state machine for training: +* +* Summary: +* +* Idle state +* | +* Waiting state <------------------- +* | | (loop back to waiting state) +* ################################### | +* # IF NEEDED: Create lexicon state # | +* # | # | +* # IF NEEDED: Create grammar state #--- +* # | # +* # Training state # +* ################################### +* | +* Idle state +* +* +* +* State: Idle state +* Event: A client calls TrainTextL, +* Action:This induces a call to CNssSpeechItemTrainer::TrainTextDealyed(). +* +* The basic idea of _delayed_training_ is that several training calls +* are piled up before taking action. When a large number of names is +* trained at a time, the economies of scale are considerable. +* +* So, TrainTextDelayed() adds the name to the to-be-trained list, +* and starts a _delay_timer_. When this delay timer has finished, the +* actual training starts. +* Transition: Idle state -> Waiting state +* +* +* State: Waiting state +* Event: A client calls TrainTextL. +* Action:The function adds the name to the to-be-trained list, and +* resets the timer +* (background: In contact synchronization via PC Suite, names are added +* every 1/2 seconds. We want to keep piling the names as long as +* new names keep coming. Since we can't wait 50 seconds +* before training, the only reasonable solution is to reset the timer +* every time a new name arrives) +* Transition: Waiting state -> Waiting state +* +* +* State: Waiting state +* Event: Timer finishes doing time. +* Action:Check the preparations for training: +* * Has the grammar for the context been created? +* * Has the lexicon for the context been created? +* CASE: Preconditions OK +* Action : Send async AddVoiceTags call +* Transition: Waiting state -> Training state +* +* State: Training state +* Event: Training finished +* Action: Save the newly created Rule IDs to the speech items. +* Commit the database. +* Make the callbacks (success or failure) to the client. +* +* Transition: Check if new names were piled during traning. +* YES: Training state -> Waiting state +* NO : Training state -> Idle state +* +*/ + +#include "srsfbldvariant.hrh" +#include "nssvascspeechitemtrainer.h" +#include "nssvasctrainingparameters.h" +#include "nssvascvasdatabase.h" +#include "nssvasmsavetagclient.h" +#include "nssvasmdeletetagclient.h" + +#include "rubydebug.h" + +#include + +#ifdef _DEBUG + // Used in UDEB only + _LIT( KSpeechItemTrainerPanic, "VasCSpeechItemTrainer" ); +#endif // _DEBUG + +#define DEBUGPANIC User::Panic( KSpeechItemTrainerPanic, __LINE__ ) +#define REACT( a, b ) if ( a != KErrNone ) { b; } +#define CHECK_NOT_NULL( a ) \ + if ( !(a) ) \ + { \ + return MNssSpeechItem::EVasTrainFailed; \ + } +#define ALLOWED_STATES( a, b, c ) \ + if ( iState != a && iState != b && iState != c ) \ + { \ + return MNssSpeechItem::EVasTrainFailed; \ + } + +// tags are buffered in the buffer that grows with this granularity +const TInt KTagBufferGranularity = 100; + +// rule ids are buffered in the buffer that grows with this granularity +const TInt KRuleIdBufferGranularity = 50; + +// tag ids are buffered in the buffer that grows with this granularity +const TInt KRemoveTagIdBufferGranularity = 50; + +// Granularity for the array of words per sinde phrase +const TInt KSindeWordArrayGranularity = 2; + + +// Local function declarations +TInt EnforceRuleIdCountInvariant( RArray& aArray, TInt aCount ); + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::CNssSpeechItemTrainer +// C++ default constructor can NOT contain any code, that +// might leave. +// --------------------------------------------------------- +// +CNssSpeechItemTrainer::CNssSpeechItemTrainer( CNssVASDatabase* aDatabase ) +: iDatabase( aDatabase ), + iSpeechItemBuffer ( NULL ), + iSpeechItemTrainingBuffer( NULL ), + iGrammarIdBuffer ( NULL ), + iGrammarIdDeletingBuffer( NULL ), + iTagBuffer(NULL) + { + // empty + } + + +// ----------------------------------------------------------------------------- +// CNssSpeechItemTrainer::ConstructL +// Symbian 2nd phase constructor can leave. +// This is overloaded function for SpeechItemTrainer from database +// ----------------------------------------------------------------------------- +// +void CNssSpeechItemTrainer::ConstructL() + { + RUBY_DEBUG_BLOCK("CNssSpeechItemTrainer::ConstructL"); + iActionTracker = CNssTrainingActionTracker::NewL( *this ); + + // Create buffer for grouping phrases in training + iSpeechItemBuffer = new(ELeave)RPointerArray; + iSpeechItemTrainingBuffer = 0; // No name being currently trained. + } + + +// ----------------------------------------------------------------------------- +// CNssSpeechItemTrainer::NewL +// Two-phased constructor. +// This is for new SpeechItemTrainer +// ----------------------------------------------------------------------------- +// +CNssSpeechItemTrainer* CNssSpeechItemTrainer::NewL( CNssVASDatabase* aDatabase ) + { + RUBY_DEBUG_BLOCK( "CNssSpeechItemTrainer::NewL" ); + + CNssSpeechItemTrainer* self = NewLC( aDatabase ); + CleanupStack::Pop( self ); + return self; + } + + +// ----------------------------------------------------------------------------- +// CNssSpeechItemTrainer::NewLC +// Two-phased constructor. +// This is overloaded function for SpeechItemTrainer from database +// ----------------------------------------------------------------------------- +// +CNssSpeechItemTrainer* CNssSpeechItemTrainer::NewLC( CNssVASDatabase* aDatabase ) + { + CNssSpeechItemTrainer* self + = new (ELeave) CNssSpeechItemTrainer( aDatabase ); + + CleanupStack::PushL( self ); + self->ConstructL(); + return self; + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::~CNssSpeechItemTrainer +// Delete the SpeechItemTrainer object and set the Portal's +// state to Terminate. +// The Trainer does not delete the Portal, the Portal +// deletes itself. +// --------------------------------------------------------- +// +CNssSpeechItemTrainer::~CNssSpeechItemTrainer() + { + RUBY_DEBUG0( "CNssSpeechItemTrainer::~CNssSpeechItemTrainer" ); + + delete iActionTracker; + + + // Buffer for grouping the MNssSpeechItems for efficient training. + if ( iSpeechItemBuffer ) + { + iSpeechItemBuffer->Reset(); + delete iSpeechItemBuffer; + } + + // Names, which are being trained by CSISpeechRecognitionUtility. + if ( iSpeechItemTrainingBuffer ) + { + iSpeechItemTrainingBuffer->Reset(); + delete iSpeechItemTrainingBuffer; + } + + // This buffer is sent to SRS Utility. Contains: The recognition phrase split into subwords. + iPhraseArray.ResetAndDestroy(); + + // Training parameters + delete iTrainingParams; + + // Context + delete iContext; + + // Rule ID array. Return values from AddVoiceTags() are placed here. + if ( iRuleIDArray ) + { + iRuleIDArray->Close(); + delete iRuleIDArray; + } + + if ( iDeleteRuleIDArray ) + { + RUBY_DEBUG0( "CNssSpeechItemTrainer::~CNssSpeechItemTrainer Deleting iDeleteRuleIDArray" ); + iDeleteRuleIDArray->Close(); + delete iDeleteRuleIDArray; + } + + // Handle to speech services + if ( iSrsApi ) + { + iSrsApi->CancelUtility(); + delete iSrsApi; + iSrsApi = NULL; + } + + // The tags to be saved: Owned by client + // RPointerArray *iTagBuffer; + if ( iTagBuffer ) + { + iTagBuffer->Reset(); + delete iTagBuffer; + iTagBuffer = 0; + } + + if ( iGrammarIdBuffer ) + { + iGrammarIdBuffer->Reset(); + delete iGrammarIdBuffer; + iGrammarIdBuffer = 0; + } + + if ( iGrammarIdDeletingBuffer ) + { + iGrammarIdDeletingBuffer->Reset(); + delete iGrammarIdDeletingBuffer; + iGrammarIdDeletingBuffer = 0; + } + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::RetrainTextDelayed +// Does delayed retraining. +// --------------------------------------------------------- +// +MNssSpeechItem::TNssSpeechItemResult CNssSpeechItemTrainer::RetrainTextDelayed( + MNssTrainTextEventHandler* aEventHandler, + CNssTrainingParameters* aTrainingParams, + CNssSpeechItem& aSpeechItem, + CNssContext& aContext) + { + RUBY_DEBUG0( "CNssSpeechItemTrainer::RetrainTextDelayed" ); + + // Check parameters: + // callback + CHECK_NOT_NULL( aEventHandler ); + + // phrase to be trained + CHECK_NOT_NULL( aSpeechItem.Text().Length() ); + + // Check state + ALLOWED_STATES( EStateIdle, ERetrainStateWaiting, ERetrainStateRetraining ); + + // Check event handler + if ( !iTrainEventHandler ) + { + iTrainEventHandler = aEventHandler; + } + else if ( iTrainEventHandler != aEventHandler ) + { + return( MNssSpeechItem::EVasTrainFailed ); + } + + // The training parameters and the context + // must be the same for all in the buffer. + TRAPD( error, + if ( !CheckTrainingParametersL( aTrainingParams ) || + !CheckContext( aContext ) ) + { + return( MNssSpeechItem::EVasTrainFailed ); + } + ); + if ( error != KErrNone ) + { + return MNssSpeechItem::EVasTrainFailed; + } + + // Buffer the training request + + // Allocate buffer, if not done earlier + if ( !SpeechItemBufferNeeded() ) + { + return( MNssSpeechItem::EVasTrainFailed ); + } + + // If this is the first tag, save Lexicon ID and Grammar ID. + if ( iSpeechItemBuffer->Count() == 0 ) + { + iLexiconId = aContext.LexiconId(); + iGrammarId = aContext.GrammarId(); + } + else + { + // The tags after the first one must have the same Gramamr ID & Lexicon ID. + if ( iLexiconId != aContext.LexiconId() || + iGrammarId != aContext.GrammarId() ) + { + return( MNssSpeechItem::EVasTrainFailed ); + } + } + + // Add request to buffer + TInt ret = iSpeechItemBuffer->Append( &aSpeechItem ); + + if ( ret != KErrNone ) + { + return( MNssSpeechItem::EVasTrainFailed ); + } + + // Restart timer. When the timer has waited for a defined period, the names + // get retrained. Timer is reseted every time RetrainTextDelayed is called. + // + // Retraining in groups is more efficient. The delay ensures this grouping. + switch( iState ) + { + case ERetrainStateRetraining: + // Wait for the retraining to finish before acting. + break; + + case EStateIdle: + SetState( ERetrainStateWaiting ); + RestartTimer(); + break; + + case ERetrainStateWaiting: + RestartTimer(); + break; + + default: + return( MNssSpeechItem::EVasTrainFailed ); + } + + return( MNssSpeechItem::EVasErrorNone ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::SaveTagDelayed +// Does delayed saving. +// --------------------------------------------------------- +// +TInt CNssSpeechItemTrainer::SaveTagDelayed( + MNssSaveTagClient* aSaveTagClient, + CNssTag& aTag ) + { + RUBY_DEBUG0( "CNssSpeechItemTrainer::SaveTagDelayed" ); + + // Check internal state: Must not be training + if ( this->iState != EStateIdle && + this->iState != ESaveStateWaiting && + this->iState != ESaveStateSaving ) + { + return( KErrNotReady ); + } + + // Check arguments + if ( aSaveTagClient == 0 ) + { + return KErrArgument; + } + + if ( !((CNssSpeechItem*)aTag.SpeechItem())->Trained() ) + { + return KErrNotReady; + } + + // The event handler must be the same for all group-saved tags + if ( !iSaveEventHandler ) + { + iSaveEventHandler = aSaveTagClient; + } + else if ( aSaveTagClient != iSaveEventHandler ) + { + return KErrArgument; + } + + // Buffer the saving request + + // Allocate buffer, if not done earlier + if ( !iTagBuffer ) + { + iTagBuffer = new RPointerArray( KTagBufferGranularity ); + + if ( !iTagBuffer ) + { + return KErrNoMemory; + } + } + + // Add request to buffer + TInt ret = iTagBuffer->Append( &aTag ); + + if ( ret != KErrNone ) + { + return( ret ); + } + + // Restart timer. When the timer has waited for a defined period, the names + // get saved. Timer is reseted every time SaveTagDelayed is called. + // + // Saving in groups is more efficient. The delay ensures this grouping. + switch( iState ) + { + case ESaveStateSaving: + // Wait for the training to finish before acting. + break; + + case EStateIdle: + SetState( ESaveStateWaiting ); + RestartTimer(); + break; + + case ESaveStateWaiting: + RestartTimer(); + break; + + default: + return( KErrCorrupt ); + } + + return( KErrNone ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::DeleteTagDelayed +// Does delayed deleting. +// (other items were commented in a header) +// --------------------------------------------------------- +// +TInt CNssSpeechItemTrainer::DeleteTagDelayed( + MNssDeleteTagClient* aDeleteTagClient, + CNssTag& aTag ) + { + RUBY_DEBUG0( "CNssSpeechItemTrainer::DeleteTagDelayed" ); + + // Check internal state: Must not be doing something else + ALLOWED_STATES( EStateIdle, EDeleteStateWaiting, EDeleteStateDeleting ); + + // Check arguments + CHECK_NOT_NULL( aDeleteTagClient ); + + // The event handler must be the same for all group-deleted tags + if ( !iDeleteEventHandler ) + { + iDeleteEventHandler = aDeleteTagClient; + } + else if ( aDeleteTagClient != iDeleteEventHandler ) + { + return KErrArgument; + } + + // Buffer the deleting request + + // Allocate buffer, if not done earlier + if ( !SpeechItemBufferNeeded() || + !GrammarIdBufferNeeded() ) + { + return( MNssSpeechItem::EVasTrainFailed ); + } + + // If this is the first tag, save the grammar id. + if ( iSpeechItemBuffer->Count() == 0 ) + { + iGrammarId = ((CNssContext*)aTag.Context())->GrammarId(); + if ( !iDeleteRuleIDArray ) + { + iDeleteRuleIDArray = new RArray; + if( !iDeleteRuleIDArray ) + { + return KErrNoMemory; + } + // no deletion. iDeleteRuleIDArray might still be used + } +// delete iDeleteRuleIDArray; // if any +// iDeleteRuleIDArray = new RArray; + } + else + { + // subsequent tags must have the same grammar id. + if ( iGrammarId != ((CNssContext*)aTag.Context())->GrammarId() ) + { + return( MNssSpeechItem::EVasTrainFailed ); + } + } + + // Add request to buffer + // iSpeechItemBuffer will be copied into iSpeechItemTrainingBuffer later and + // iSpeechItemTrainingBuffer will be used to delete tags from the VAS DB + TInt ret = iSpeechItemBuffer->Append( (CNssSpeechItem*)aTag.SpeechItem() ); + if ( ret != KErrNone ) + { + return( MNssSpeechItem::EVasTrainFailed ); + } + + RUBY_DEBUG1( "CNssSpeechItemTrainer::DeleteTagDelayed Adding [%d] to iDeleteRuleIDArray", + ( ( CNssSpeechItem* )aTag.SpeechItem() )->RuleID() ); + + ret = iDeleteRuleIDArray->Append( ( ( CNssSpeechItem* )aTag.SpeechItem() )->RuleID() ); + + if ( ret != KErrNone ) + { + RUBY_DEBUG1( "CNssSpeechItemTrainer::DeleteTagDelayed Adding to iDeleteRuleIDArray failed with error [%d]", ret ); + return( MNssSpeechItem::EVasTrainFailed ); + } + + // Restart timer. When the timer has waited for a defined period, the names + // get deleted. Timer is reseted every time DeleteDagDelayed is called. + // + // Deleting in groups is more efficient. The delay ensures this grouping. + switch( iState ) + { + case EDeleteStateDeleting: + // Wait for the training to finish before acting. + break; + + case EStateIdle: + SetState( EDeleteStateWaiting ); + RestartTimer(); + break; + + case EDeleteStateWaiting: + RestartTimer(); + break; + + default: + return( KErrCorrupt ); + } + + return( KErrNone ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::TrainTextDelayed +// Does delayed training. +// --------------------------------------------------------- +// +MNssSpeechItem::TNssSpeechItemResult CNssSpeechItemTrainer::TrainTextDelayed( + MNssTrainTextEventHandler* aEventHandler, + CNssTrainingParameters* aTrainingParams, + CNssSpeechItem& aSpeechItem, + CNssContext& aContext) + { + RUBY_DEBUG0( "CNssSpeechItemTrainer::TrainTextDelayed" ); + + // Check parameters: + // callback + CHECK_NOT_NULL( aEventHandler ); + + // Text is not null + CHECK_NOT_NULL( aSpeechItem.Text().Length() ); + + // Check state + ALLOWED_STATES( EStateIdle, ETrainStateWaiting, ETrainStateTraining ); + + // Check event handler + if ( !iTrainEventHandler ) + { + iTrainEventHandler = aEventHandler; + } + else if ( iTrainEventHandler != aEventHandler ) + { + return( MNssSpeechItem::EVasTrainFailed ); + } + + // The training parameters and the context + // must be the same for all in the buffer. + TRAPD( error, + if ( !CheckTrainingParametersL( aTrainingParams ) || + !CheckContext( aContext ) ) + { + return( MNssSpeechItem::EVasTrainFailed ); + } + ); + if ( error != KErrNone ) + { + return MNssSpeechItem::EVasTrainFailed; + } + + // Buffer the training request + if ( !SpeechItemBufferNeeded() ) + { + return( MNssSpeechItem::EVasTrainFailed ); + } + + TInt ret = iSpeechItemBuffer->Append( &aSpeechItem ); + + if ( ret == KErrNoMemory ) + { + return( MNssSpeechItem::EVasTrainFailed ); + } + + // Restart timer. When the timer has waited for a defined period, the names + // get trained. Timer is reseted every time TrainTextDelayed is called. + // + // Training in groups is more efficient. The delay ensures this grouping. + switch( iState ) + { + case ETrainStateTraining: + // Wait for the training to finish before acting. + break; + + case EStateIdle: + SetState( ETrainStateWaiting ); + RestartTimer(); + break; + + case ETrainStateWaiting: + RestartTimer(); + break; + + default: + return( MNssSpeechItem::EVasTrainFailed ); + } + + return( MNssSpeechItem::EVasErrorNone ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::HandleTrainError +// Called if AddVoiceTags() call failed. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::HandleTrainError( RPointerArray*& anItemArray ) + { + RUBY_DEBUG0( "CNssSpeechItemTrainer::HandleTrainError" ); + + TInt count = anItemArray->Count(); + + CleanUpTraining(); + + // Announce the error to the client + for( TInt k=0; kHandleTrainComplete( KErrGeneral ); + + } + iTrainEventHandler = NULL; + // All the errors reported, everything was rolled back + // Back tothe idle state + SetState( EStateIdle ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::CleanUpTrianing +// Cleans memory allocated by training. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::CleanUpTraining(void) + { + // Delete buffered names + if ( iSpeechItemTrainingBuffer ) + { + iSpeechItemTrainingBuffer->Close(); + } + delete iSpeechItemTrainingBuffer; + iSpeechItemTrainingBuffer = 0; + + // Delete parameters common to all names + delete iTrainingParams; + iTrainingParams = 0; + + delete iContext; + iContext = 0; + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::DoTrainTextDelayed +// Starts training tags. Sends the AddVoiceTags() call. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::DoTrainTextDelayed() + { + SetState( ETrainStateTraining ); + + // Move from speech item buffers from filling state to processing state. + + // names to be trained + names to be retrained + iSpeechItemTrainingBuffer = iSpeechItemBuffer; + iSpeechItemBuffer = 0; + + // Initialize iSrsApi (speech recognition utility) + TInt error = CreateSrsApi(); + if ( error != KErrNone ) + { + RUBY_DEBUG1( "CNssSpeechItemTrainer::DoTrainTextDelayed Creating SrsApi failed. Error [%d]", error ); + HandleTrainError( iSpeechItemTrainingBuffer ); + } + else + { + + __ASSERT_DEBUG( iContext->LexiconId() != KInvalidLexiconID, + User::Panic( KSpeechItemTrainerPanic, KErrCorrupt ) ); + __ASSERT_DEBUG( iContext->GrammarId() != KInvalidGrammarID, + User::Panic( KSpeechItemTrainerPanic, KErrCorrupt ) ); + + // Extract texts from speech items to phrase array. + + // iPhraseArray is an array of arrays: + // + // |-- firstname1 + // tag1 -|-- lastname1 + // + // tag2 -|-- firstname2 + // |-- lastname2 + iPhraseArray.ResetAndDestroy(); + + // Convert speech item array to the format understood by the Utility. + TInt ret = SpeechItems2Phrases( *iSpeechItemTrainingBuffer, iPhraseArray ); + if ( ret != KErrNone ) + { + SendTrainingCallbacks( ret ); + /** @todo reengineer into signle return path + @todo check that everything is cleaned up before the return */ + return; + } + + // Initialize Rule ID array. The Utility puts the IDs + // of newly created rules here. + // Not using (ELeave) since this is non-leaving function (trapping of (ELeave) is not allowed). + iRuleIDArray = new RArray; + + if ( iRuleIDArray == 0 ) + { + RUBY_DEBUG0( "CNssSpeechItemTrainer::DoTrainTextDelayed iRuleIDArray == 0" ); + + HandleTrainError( iSpeechItemTrainingBuffer ); + } + else + { + RUBY_DEBUG1( "CNssSpeechItemTrainer::Calling AddVoiceTags (%d names)", + iPhraseArray.Count() ); + // allocation succeeded + + TRAPD( errSinde, const RArray& languages = + iTrainingParams->SindeLanguagesL() ); + + if ( errSinde == KErrNone ) + { +#ifdef __SINDE_TRAINING + TRAP_IGNORE( + const RArray& languages = + iTrainingParams->SindeLanguagesL(); + ret = iSrsApi->AddVoiceTags( iPhraseArray, + languages, + (TSILexiconID)iContext->LexiconId(), + (TSIGrammarID)iContext->GrammarId(), + *iRuleIDArray ); + ); // TRAP_IGNORE +#endif // __SINDE_TRAINING + } + else + { + // Use the old style of training if SINDE languages are not available + ret = iSrsApi->AddVoiceTags( iPhraseArray, + iTrainingParams->Languages(), + (TSILexiconID)iContext->LexiconId(), + (TSIGrammarID)iContext->GrammarId(), + *iRuleIDArray ); + } + + // error -> cleanup + if ( ret != KErrNone ) + { + RUBY_DEBUG1( "CNssSpeechItemTrainer::AddVoiceTags failed(%d)", ret ); + + HandleTrainComplete( ret ); + } // if ret is not KErrNone + } // if iRuleIDArray allocation succeeded + } // if SRS API was created successfully + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::DoRetrainTextDelayed +// Starts the 1st phase of retraining, +// which is removing old rules. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::DoRetrainTextDelayed() + { + __ASSERT_DEBUG( iSpeechItemBuffer != 0, DEBUGPANIC ); + + SetState( ERetrainStateRetraining ); + + // Move speech item buffer from filling state to processing state. + iSpeechItemTrainingBuffer = iSpeechItemBuffer; + iSpeechItemBuffer = 0; + + // Initialize iSrsApi (speech recognition utility) + TInt error = CreateSrsApi(); + if ( error != KErrNone ) + { + RUBY_DEBUG1( "CNssSpeechItemTrainer::DoRetrainTextDelayed Creating SrsApi failed. Error [%d]", error ); + HandleTrainError( iSpeechItemTrainingBuffer ); + return; + } + + // Start deleting tags + iTagDeleteCounter = 0; + DeleteNextTag(); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::DoRetrainAddVoiceTags +// Phase 2 of retraining: Add voice tags +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::DoRetrainAddVoiceTags() + { + SetState( ERetrainStateRetraining ); + + // Move from speech item buffers from filling state to processing state. + + __ASSERT_DEBUG( iContext->LexiconId() != KInvalidLexiconID, + User::Panic( KSpeechItemTrainerPanic, KErrCorrupt ) ); + __ASSERT_DEBUG( iContext->GrammarId() != KInvalidGrammarID, + User::Panic( KSpeechItemTrainerPanic, KErrCorrupt ) ); + + // Extract texts from speech items to phrase array. + + iPhraseArray.ResetAndDestroy(); + + // Convert speech item array to the format understood by the Utility. + TInt ret = SpeechItems2Phrases( *iSpeechItemTrainingBuffer, iPhraseArray ); + if ( ret != KErrNone ) + { + SendTrainingCallbacks( ret ); + return; + } + + // Allocate Rule ID array. The Utility fills this array. + // When the utility trans a name, it attaches a Rule ID to the name. + iRuleIDArray = new RArray; + if ( iRuleIDArray == 0 ) + { + SendTrainingCallbacks( KErrNoMemory ); + return; + } + + RUBY_DEBUG1( "CNssSpeechItemTrainer::Retrain Calling AddVoiceTags (%d names)", + iPhraseArray.Count() ); + + TRAPD( errSinde, const RArray& languages = + iTrainingParams->SindeLanguagesL() ); + + if ( errSinde == KErrNone ) + { +#ifdef __SINDE_TRAINING + TRAP_IGNORE( + const RArray& languages = + iTrainingParams->SindeLanguagesL(); + ret = iSrsApi->AddVoiceTags( iPhraseArray, + languages, + (TSILexiconID)iContext->LexiconId(), + (TSIGrammarID)iContext->GrammarId(), + *iRuleIDArray ); + ); // TRAP_IGNORE +#endif // __SINDE_TRAINING + } + else + { + // Use the old style of training if SINDE languages are not available + ret = iSrsApi->AddVoiceTags( iPhraseArray, + iTrainingParams->Languages(), + (TSILexiconID)iLexiconId, + (TSIGrammarID)iGrammarId, + *iRuleIDArray ); + } + + // error -> cleanup + if ( ret != KErrNone ) + { + RUBY_DEBUG1( "CNssSpeechItemTrainer::Retrain ERROR: AddVoiceTags(%d)", ret ); + + SendTrainingCallbacks( ret ); + } + + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::DoRetrainAddVoiceTags +// After the new tags have been trained: +// Announce the new rule IDs for speech items, +// and update the rule IDs to the VAS database. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::HandleRetrainComplete( TInt aResult ) + { + RUBY_DEBUG1( "CNssSpeechItemTrainer::HandleTrainComplete(%d)", aResult ); + + // iRuleIdArray contains the rule IDs of the newly trained tags. + // The following call makes sure that + // iRuleIdArray.Count() == iSpeechItemTrainingBuffer.Count(). + if ( EnforceRuleIdCountInvariant( *iRuleIDArray, + iSpeechItemTrainingBuffer->Count() ) != KErrNone ) + { + SendTrainingCallbacks( KErrNoMemory ); + } + else + { + TInt count = iSpeechItemTrainingBuffer->Count(); + RArray ruleIdUpdateArray( KRuleIdBufferGranularity ); + + for( TInt itemIdx = 0; itemIdx < count; itemIdx++ ) + { + CNssSpeechItem* item = (*iSpeechItemTrainingBuffer)[itemIdx]; + TUint32 ruleID = (*iRuleIDArray)[itemIdx]; + if ( ruleID != KInvalidRuleID ) + { + // tag was trained successfully + + item->DelayedTrainingComplete( ruleID ); + + TNssSpeechItem flatItem; + + flatItem.iRuleId = item->RuleID(); + flatItem.iTagId = item->TagId(); + + TInt ret = ruleIdUpdateArray.Append( flatItem ); + + if ( ret != KErrNone ) + { + ruleIdUpdateArray.Close(); + SendTrainingCallbacks( KErrNoMemory ); + return; + } + } + else + { + // training failed -> remove tag + iDatabase->DeleteTag( item->TagId() ); + } + } + + aResult = iDatabase->UpdateTagRuleIDs( ruleIdUpdateArray ); + ruleIdUpdateArray.Close(); + SendTrainingCallbacks( aResult ); + } + + + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::DoSaveTags +// Saves tags. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::DoSaveTags() + { + + __ASSERT_DEBUG( iState == ESaveStateWaiting, DEBUGPANIC ); + + // Save the tags en masse + TInt ret = iDatabase->SaveTags( iTagBuffer ); + + // Next, we're going to make callbacks to the client. + // Before that, we have to return the object to idle state, + // This way, the last callback can trigger new train/save action. + + // Destroy buffered tags + TInt count = iTagBuffer->Count(); + iTagBuffer->Close(); + delete iTagBuffer; + iTagBuffer = 0; + + // Detach save tag client + MNssSaveTagClient* client = iSaveEventHandler; + iSaveEventHandler = 0; + + // Make SpeechItemTrainer ready for new challenges + SetState( EStateIdle ); + + // Make callbacks + for ( TInt k = 0; k < count; k++ ) + { + if ( ret == KErrNone ) + { +#ifdef _DEBUG + TRAPD( err, client->SaveTagCompleted( KErrNone ) ); + __ASSERT_DEBUG( err == KErrNone, DEBUGPANIC ); +#else + client->SaveTagCompleted( KErrNone ); +#endif // _DEBUG + } + else + { +#ifdef _DEBUG + TRAPD( err, client->SaveTagCompleted( KErrGeneral ) ); + __ASSERT_DEBUG( err == KErrNone, DEBUGPANIC ); +#else + client->SaveTagCompleted( KErrGeneral ); +#endif // _DEBUG + } + } + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::DoDeleteTags +// Starts tag removal. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::DoDeleteTags() + { + // NOTE: This method is never called from anywhere! + + SetState( EDeleteStateDeleting ); + + TInt err = CreateSrsApi(); + if ( err != KErrNone ) + { + RemoveTagsFinished( err ); + return; + } + + // Move Tag Buffer from filling state to processing state. + iSpeechItemTrainingBuffer = iSpeechItemBuffer; + iSpeechItemBuffer = 0; + iSpeechItemBuffer = new RPointerArray; // New filling buffer + if( !iSpeechItemBuffer ) + { + SendTrainingCallbacks( KErrNoMemory ); + return; + } + + iTagDeleteCounter = 0; + DeleteNextTag(); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::DoDeleteGroupedTags +// Starts grouped tag removal. +// Used when deleting tags without retraining. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::DoDeleteGroupedTags() + { + RUBY_DEBUG1("CNssSpeechItemTrainer::DoDeleteGroupedTags iDeleteRuleIDArray->Count() = [%d]", iDeleteRuleIDArray->Count() ); + SetState( EDeleteStateDeleting ); + + // Move Tag Buffer from filling state to processing state. + // We want to have this buffer != NULL so that we are able to do callbacks (with appropriate error code ) + // in RemoveTagsFinished method even if e.g. CreateSrsApi fails. + iSpeechItemTrainingBuffer = iSpeechItemBuffer; + iSpeechItemBuffer = 0; + + TInt err = CreateSrsApi(); + if ( err != KErrNone ) + { + RemoveTagsFinished( err ); + return; + } + + iSpeechItemBuffer = new RPointerArray; // New filling buffer + + if( !iSpeechItemBuffer ) + { + // As this method is not used when retraining, no need + // to send other callbacks than those related to deleting tags. + RemoveTagsFinished( KErrNoMemory ); + return; + } + + TInt ret = iSrsApi->RemoveRules( ( TSIGrammarID )iGrammarId, + *iDeleteRuleIDArray ); + + if ( ret != KErrNone && ret != KErrNotFound ) + { + RUBY_DEBUG1( "CNssSpeechItemTrainer::DoDeleteGroupedTags RemoveRules failed. \ + Error [%d]", ret ); + RemoveTagsFinished( ret ); + return; + } + + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::DeleteNextTag +// Sends a RemoveRule() call for the next rule in the removal queue. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::DeleteNextTag() + { + RUBY_DEBUG2( "CNssSpeechItemTrainer::DeleteNextTag iTagDeleteCounter [%d], iSpeechItemTrainingBuffer->Count() [%d]", iTagDeleteCounter, iSpeechItemTrainingBuffer->Count() ); + + __ASSERT_DEBUG( iTagDeleteCounter <= iSpeechItemTrainingBuffer->Count(), + User::Panic( KSpeechItemTrainerPanic, __LINE__ ) ); + + CNssSpeechItem* speechItem = (*iSpeechItemTrainingBuffer)[iTagDeleteCounter]; + + TInt ret = iSrsApi->RemoveRule( + (TSIGrammarID)speechItem->GrammarId(), + (TSIRuleID)speechItem->RuleID() + ); + + if ( ret != KErrNone && ret != KErrNotFound ) + { + RemoveTagsFinished( ret ); + return; + } + } + + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::HandleRemoveTagComplete +// Called after SRS has successfully removed a tag. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::HandleRemoveTagComplete() + { + iTagDeleteCounter++; + + if ( iTagDeleteCounter < iSpeechItemTrainingBuffer->Count() ) + { + DeleteNextTag(); + } + else + { + RemoveTagsFinished( KErrNone ); + return; + } + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::HandleRemoveTagsComplete +// Called after SRS has successfully removed a group of tags. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::HandleRemoveTagsComplete() + { + RemoveTagsFinished( KErrNone ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::HandleRemoveTagFailed +// Called if SRS fails to remove a tag. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::HandleRemoveTagFailed( TInt aError ) + { + RemoveTagsFinished( aError ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::RemoveTagsFinished +// All required tags have been removed. +// Go to next phase. +// functions a bit defferently depending on whether we were retraining or +// deleting tags +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::RemoveTagsFinished( TInt aSuccess ) + { + // The continuation depends on what we are doing: + + // If we are deleting tags + if ( iState == EDeleteStateDeleting ) + { + if ( aSuccess == KErrNone ) + { + RemoveTagsFromVAS(); + } + else{ + FinishDeleteTags( aSuccess ); + } + } + + // If we are retraining + // (removing the old rules before adding the new ones) + else if ( iState == ERetrainStateRetraining ) + { + if ( aSuccess == KErrNone ) + { + DoRetrainAddVoiceTags(); + } + else{ + SendTrainingCallbacks( aSuccess ); + } + } + else + { + RUBY_DEBUG1( "CNssSpeechItemTrainer::RemoveTagsFinished - ERROR: Wrong state(%d)", iState ); + } + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::RemoveTagsFromVAS +// Deletes tags from VAS. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::RemoveTagsFromVAS() + { + RArray tagIdArray( KRemoveTagIdBufferGranularity ); + TInt ret = KErrNone; + + ////////////// Remove tags from VAS DB /////////////////// + + // Take the Tag IDs to an array. + for ( TInt k( 0 ); k < iSpeechItemTrainingBuffer->Count(); k++ ) + { + ret |= tagIdArray.Append( (*iSpeechItemTrainingBuffer)[k]->TagId() ); + } + + // Delete tags from VAS DB + if ( ret == KErrNone ) + { + ret = iDatabase->DeleteTags( tagIdArray ); + } + + // Close tag ID array. + tagIdArray.Close(); + + ////////////// Remove tags from VAS DB done ////////////// + + FinishDeleteTags( KErrNone ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::FinishDeleteTags +// Cleans up and sends calbacks after +// deleting has finished or completed. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::FinishDeleteTags( TInt aSuccess ) + { + RUBY_DEBUG1("CNssSpeechItemTrainer::FinishDeleteTags aSuccess [%d]", aSuccess); + // Commit or uncommit according to success + if ( iSrsApi ) + { + if ( aSuccess == KErrNone ) + { + CommitSIUpdate(); + } + else{ + UncommitSIUpdate(); + } + } + + // Free memory + TInt count( 0 ); + if ( iSpeechItemTrainingBuffer ) + { + count = iSpeechItemTrainingBuffer->Count(); + RUBY_DEBUG1("CNssSpeechItemTrainer::FinishDeleteTags aSuccess Freeing [%d] items from the training buffer", count); + iSpeechItemTrainingBuffer->Close(); + } + delete iSpeechItemTrainingBuffer; + iSpeechItemTrainingBuffer = 0; + + // Send callbacks + MNssDeleteTagClient* client = iDeleteEventHandler; + iDeleteEventHandler = 0; + iTagDeleteCounter = 0; + + + for ( TInt k( 0 ); k < count; k++ ) + { + RUBY_DEBUG1("CNssSpeechItemTrainer::FinishDeleteTags sending callback for item [%d]", k); + // Failed or succeedeed, one rule is processed + iDeleteRuleIDArray->Remove(0); + + client->DeleteTagCompleted( aSuccess ); + } + RUBY_DEBUG2("CNssSpeechItemTrainer::FinishDeleteTags Reported [%d] deletions. [%d] remaining. Setting idle state", count, iDeleteRuleIDArray->Count()); + SetState( EStateIdle ); + + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::RunBufferedActionsL +// Called when it is time to execute the buffered actions +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::RunBufferedActionsL() + { + RUBY_DEBUG_BLOCK( "CNssSpeechItemTrainer::RunL" ); + + if ( iState == ETrainStateWaiting ) + { + DoTrainTextDelayed(); + } + else if ( iState == ESaveStateWaiting ) + { + DoSaveTags(); + } + else if ( iState == EDeleteStateWaiting ) + { + DoDeleteGroupedTags(); + } + else if ( iState == ERetrainStateWaiting ) + { + DoRetrainTextDelayed(); + } + else + { + RUBY_DEBUG1( "CNssSpeechItemTrainer::RunL - ERROR: Wrong state (%d)", iState ); + } + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::RestartTimer +// Restarts the timer. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::RestartTimer() + { + TRAP_IGNORE( iActionTracker->ActionRequestedL() ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::CheckTrainingParametersL +// All tags in the queue should have the same training parameters. This +// function check that a new context has similar training parameters +// as the previous ones. +// --------------------------------------------------------- +// +TBool CNssSpeechItemTrainer::CheckTrainingParametersL( const CNssTrainingParameters* aParams ) + { + if ( aParams ) + { + if ( !iTrainingParams ) + { + // Make a local copy of training parameters + iTrainingParams = CNssTrainingParameters::NewL(); + + const RArray& paramLangArray = aParams->Languages(); + + RArray* langArray = new ( ELeave ) RArray; + CleanupStack::PushL( langArray ); + + for ( TInt k = 0; k < paramLangArray.Count(); k++ ) + { + TInt ret = langArray->Append( paramLangArray[ k ] ); + if ( ret != KErrNone ) + { + langArray->Close(); + delete langArray; + return EFalse; + } + } + + // Copy training languages + iTrainingParams->SetLanguages( langArray ); + CleanupStack::Pop( langArray ); + + // Copy SINDE languages + TRAP_IGNORE( iTrainingParams->SetSindeLanguages( aParams->SindeLanguagesL() ) ); + + // Copy separator + iTrainingParams->SetSeparator( aParams->Separator() ); + } + else + { + // Compare with previous training parameters + + // Separator + if ( aParams->Separator() != iTrainingParams->Separator() ) + { + return EFalse; + } + + // Training languages + for ( TInt k = 0; k < aParams->Languages().Count(); k++ ) + { + if ( aParams->Languages()[ k ] != iTrainingParams->Languages()[ k ] ) + { + return EFalse; + } + } + + // @todo SINDE languages should be checked also! + } + } + + return ETrue; + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::CheckContext +// All tags in the queue should have the same contexts. This +// function check that a new context is similar to that of previous tags. +// --------------------------------------------------------- +// +TBool CNssSpeechItemTrainer::CheckContext(CNssContext& aContext) + { + if ( &aContext == 0 ) // Default settings - anything goes. + { + return( EFalse ); + } + + if ( aContext.RecognitionMode() != ENSSSdSiMode && + aContext.RecognitionMode() != ENSSSiMode ) + { + return( EFalse ); + } + + if ( iContext ) + { + // Check that identifiers (name & ID) are identical. + if ( aContext.ContextId() != iContext->ContextId() ) + { + return( EFalse ); + } + + if ( aContext.ContextName().Compare( iContext->ContextName() ) != 0 ) + { + return( EFalse ); + } + } + else + { + if ( aContext.RecognitionMode() == ENSSSdSiMode ) + { + aContext.SetRecognitionMode( ENSSSiMode ); + } + + TRAPD( error, iContext = aContext.CopyL() ); + if ( error != KErrNone ) + { + return EFalse; + } + } + + return( ETrue ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::HandleTrainComplete +// Called after speech recognition utility has trained all tags. +// Announces the newly assigned Rule IDs for the speech items. +// Signals the VAS client that training has been finished. +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::HandleTrainComplete( TInt aResult ) + { + RUBY_DEBUG1( "CNssSpeechItemTrainer::HandleTrainComplete(%d)", aResult ); + + // iRuleIdArray contains the rule IDs of the newly trained tags. + // The following call makes sure that + // iRuleIdArray.Count() == iSpeechItemTrainingBuffer.Count(). + if ( EnforceRuleIdCountInvariant( *iRuleIDArray, + iSpeechItemTrainingBuffer->Count() ) != KErrNone ) + { + SendTrainingCallbacks( KErrNoMemory ); + } + + // Save rule IDs. Some rule IDs may have "KInvalidRuleID". This is a sign + // that training failed. It's good to save also that info to speech items. + for( TInt itemIdx = 0; itemIdx < iSpeechItemTrainingBuffer->Count(); itemIdx++ ) + { + CNssSpeechItem* item = (*iSpeechItemTrainingBuffer)[ itemIdx ]; + TUint32 ruleID = (*iRuleIDArray)[ itemIdx ]; + + item->DelayedTrainingComplete( ruleID ); + } + + SendTrainingCallbacks( aResult ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::SendTrainingCallbacks +// +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::SendTrainingCallbacks( TInt aResult ) + { + // If the changes were successful, commit them. + // If they werent, roll back the changes. + if ( aResult == KErrNone ) + { + CommitSIUpdate(); + } + else{ + UncommitSIUpdate(); + } + + // Save count (callback count)... + TInt count = iSpeechItemTrainingBuffer->Count(); + MNssTrainTextEventHandler* callback = iTrainEventHandler; + RArray* ruleIdArray = iRuleIDArray; + iRuleIDArray = 0; + + // Delete buffered names that were just trained + if ( iSpeechItemTrainingBuffer ) + { + iSpeechItemTrainingBuffer->Close(); + } + delete iSpeechItemTrainingBuffer; + iSpeechItemTrainingBuffer = 0; + + + // If there are names on the training queue, + // restart the waiting of delayed training. + if ( iSpeechItemBuffer && iSpeechItemBuffer->Count() > 0 ) + { + // don't delete context etc as training will be continued + SetState( ETrainStateWaiting ); + RestartTimer(); + } + else + { + SetState( EStateIdle ); + + // Cleanup + delete iTrainingParams; + iTrainingParams = 0; + + delete iContext; + iContext = 0; + + iTrainEventHandler = 0; + } + + // Make the callbacks to the client according to the recognition result. + for( TInt k( 0 ); k < count; k++ ) + { + if ( ruleIdArray->Count() <= k ) + { + RUBY_DEBUG2( "CNssSpeechItemTrainer::SendTrainingCallbacks ruleIdArray->Count() [%d] <= k [%d]", ruleIdArray->Count(), k ); + callback->HandleTrainComplete( KErrNoMemory ); + } + if ( (*ruleIdArray)[ k ] == KInvalidRuleID ) + { + RUBY_DEBUG0( "CNssSpeechItemTrainer::SendTrainingCallbacks (*ruleIdArray)[ k ] == KInvalidRuleID" ); + if ( aResult == KErrNone ) + { + callback->HandleTrainComplete( KErrGeneral ); + } + else + { + callback->HandleTrainComplete( aResult ); + } + + } + else + { + callback->HandleTrainComplete( KErrNone ); + } + } + + ruleIdArray->Close(); + delete ruleIdArray; + ruleIdArray = 0; + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::MsruoEvent +// SRS Utility callback function +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::MsruoEvent( TUid aEvent, TInt aResult ) + { + switch( aEvent.iUid ) + { + case KUidAsrEventAddVoiceTagsVal: + RUBY_DEBUG0( "CNssSpeechItemTrainer::MsruoEvent - state is KUidAsrEventAddVoiceTagsVal" ); + + if ( iState == ETrainStateTraining ) + { + HandleTrainComplete( aResult ); + } + else if ( iState == ERetrainStateRetraining ) + { + HandleRetrainComplete( aResult ); + } + break; + + case KUidAsrEventRemoveRuleVal: + RUBY_DEBUG0( "CNssSpeechItemTrainer::MsruoEvent - state is KUidAsrEventRemoveRuleVal" ); + + if ( aResult == KErrNone || aResult == KErrNotFound ) + { + HandleRemoveTagComplete(); + } + else{ + HandleRemoveTagFailed( aResult ); + } + break; + + case KUidAsrEventRemoveRulesVal: + RUBY_DEBUG0( "CNssSpeechItemTrainer::MsruoEvent - state is KUidAsrEventRemoveRulesVal" ); + + if( aResult == KErrNone || aResult == KErrNotFound ) + { + HandleRemoveTagsComplete(); + } + else + { + HandleRemoveTagFailed( aResult ); + } + break; + + default: + RUBY_DEBUG2( "CNssSpeechItemTrainer::MsruoEvent - ERROR: Uknown state (%d,%d)", aEvent.iUid, aResult ); + break; + } + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::SplitPhraseSindeL +// Splits a phrase to parts. Returns the parts in array. +// --------------------------------------------------------- +// +CDesC16ArrayFlat* CNssSpeechItemTrainer::SplitPhraseSindeL( const TDesC& aPhrase, + TChar aSeparator ) + { + CDesC16ArrayFlat* wordArray = new ( ELeave ) CDesC16ArrayFlat( KSindeWordArrayGranularity ); + CleanupStack::PushL( wordArray ); + + // Separate the words in the phrase. + TPtrC text = aPhrase; // Cursor to the phrase + + do + { + TInt index = text.Locate( aSeparator ); + + if ( index != 0 ) + { + // no more separators -> this is the last word -> end of word == end of string + if ( index == KErrNotFound ) + { + index = text.Length(); + } + + // Add word to array + wordArray->AppendL( text.Left( index ) ); + } + + // Discard the processed word + TInt charactersLeft = text.Length() - index; + // Forget the separator + if ( charactersLeft > 0 ) + { + charactersLeft--; + } + + // Update text cursor + text.Set( text.Right( charactersLeft ) ); + + } + while( text.Length() > 0 ); + + CleanupStack::Pop( wordArray ); + return wordArray; + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::SplitPhrase +// Splits a phrase to parts. Returns the parts in array. +// --------------------------------------------------------- +// +CDesC16ArrayFlat* CNssSpeechItemTrainer::SplitPhraseL( const TDesC& aPhrase, TChar aSeparator ) + { + CDesC16ArrayFlat *wordArray = new (ELeave) CDesC16ArrayFlat( KSindeWordArrayGranularity ); + + // Separate the words in the phrase. Especially firstname / lastname. + TPtrC text = aPhrase; // Cursor to the phrase + + do + { + TInt index = text.Locate( aSeparator ); + + // no more separators -> this is the last word -> end of word == end of string + if ( index == -1 ) + { + index = text.Length(); + } + + // Add word to array + wordArray->AppendL( text.Left( index ) ); + + // Discard the processed word + TInt charactersLeft = text.Length() - index; + // Forget the separator + // (the last word doesn't have one) + if ( charactersLeft > 0 ) + { + charactersLeft--; + } + + // Update text cursor + text.Set( text.Right( charactersLeft ) ); + + } + while( text.Length() > 0 ); + + return wordArray; + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::CommitSIUpdate +// Commits changes to SRS and destroys SRS utility. +// --------------------------------------------------------- +// +TInt CNssSpeechItemTrainer::CommitSIUpdate() + { + if ( !iSrsApi ) + { + return( KErrNotReady ); + } + + iSrsApi->CommitChanges(); + + delete iSrsApi; + iSrsApi = NULL; + + return( KErrNone ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::UncommitSIUpdate +// Destroys SRS utility without committing. +// --------------------------------------------------------- +// +TInt CNssSpeechItemTrainer::UncommitSIUpdate() + { + if ( !iSrsApi ) + { + return( KErrNotReady ); + } + + delete iSrsApi; + iSrsApi = NULL; + + return( KErrNone ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::SpeechItemBufferNeeded +// Trys to allocate a speech item buffer +// --------------------------------------------------------- +// +TBool CNssSpeechItemTrainer::SpeechItemBufferNeeded() + { + if ( !iSpeechItemBuffer ) + { + iSpeechItemBuffer = new RPointerArray; + if ( !iSpeechItemBuffer ) + { + return( EFalse ); + } + } + + return( ETrue ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::SpeechItemBufferNeeded +// Trys to allocate a grammar id buffer +// --------------------------------------------------------- +// +TBool CNssSpeechItemTrainer::GrammarIdBufferNeeded() + { + if ( !iGrammarIdBuffer ) + { + iGrammarIdBuffer = new RArray; + if ( !iGrammarIdBuffer ) + { + return( EFalse ); + } + } + + return( ETrue ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::SpeechItemBufferNeeded +// Trys to create SRS api. Returns success status. +// --------------------------------------------------------- +// +TInt CNssSpeechItemTrainer::CreateSrsApi() + { + if ( !iSrsApi ) + { + TRAPD( err, iSrsApi = CNssSiUtilityWrapper::NewL( *this, KNssVASApiUid ) ); + REACT( err, return( err ) ); + + iSrsApi->SetEventHandler( this ); + } + + return KErrNone; + } + + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::SpeechItems2Phrases +// Converts an array of speech items +// into Utility's AddVoiceTags array format. +// --------------------------------------------------------- +// +TInt CNssSpeechItemTrainer::SpeechItems2Phrases( + RPointerArray& aSpeechItems, + RPointerArray& aPhrases ) + { + // iPhraseArray is an array of arrays: + // + // |-- firstname1 + // tag1 -|-- lastname1 + // + // tag2 -|-- firstname2 + // |-- lastname2 + + // Create training parameters, if none were given + if ( iTrainingParams == 0 ) + { + RArray* languages = new RArray(); + + if ( !languages ) + { + return( KErrNoMemory ); + } + + // Use UI language + 1 other as default if languages are not given + // in training parameters + TInt err = languages->Append( User::Language() ); + + if ( err == KErrNone ) + { + err = languages->Append( ELangOther ); + } + + if ( err != KErrNone ) + { + languages->Close(); + delete languages; + return( err ); + } + + TRAP( err, iTrainingParams = CNssTrainingParameters::NewL() ); + if ( err != KErrNone ) + { + languages->Close(); + delete languages; + return( err ); + } + + iTrainingParams->SetLanguages( languages ); + } + + // Split the phrases to subwords (names -> first name + last name) + for ( TInt k( 0 ); k < aSpeechItems.Count(); k++ ) + { + // Split names into the first name and the last name + CDesC16ArrayFlat *wordArray = 0; + + // Check if SINDE type of training should be used + TRAPD( errSinde, const RArray& temp = + iTrainingParams->SindeLanguagesL() ); + if ( errSinde == KErrNone ) + { + TRAP_IGNORE( wordArray = SplitPhraseSindeL( aSpeechItems[ k ]->RawText(), + iTrainingParams->Separator() ) ); + } + else + { + TRAP_IGNORE( wordArray = SplitPhraseL( aSpeechItems[ k ]->Text(), + iTrainingParams->Separator() ) ); + } + + if ( wordArray == 0 ) + { + return KErrNoMemory; + } + + TInt ret = aPhrases.Append( wordArray ); + + if ( ret != KErrNone ) + { + return ret; + } + } + + return( KErrNone ); + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::SetState +// Changes state of the trainer +// --------------------------------------------------------- +// +void CNssSpeechItemTrainer::SetState(TTrainState aState) + { + RUBY_DEBUG2("CNssSpeechItemTrainer::SetState Switching state from [%d] to [%d]", iState, aState); +#ifdef _DEBUG + if( iState == EDeleteStateWaiting ) + { + RUBY_DEBUG0("CNssSpeechItemTrainer::SetState From EDeleteStateWaiting"); + } + if( aState == EDeleteStateWaiting ) + { + RUBY_DEBUG0("CNssSpeechItemTrainer::SetState To EDeleteStateWaiting"); + } + +#endif + iState = aState; + } + +// --------------------------------------------------------- +// CNssSpeechItemTrainer::EnforceRuleIdCountInvariant +// Adds KInvalidRuleId to the Rule ID Array, until +// the number of rules is equal to count. +// --------------------------------------------------------- +// +TInt EnforceRuleIdCountInvariant( RArray& aArray, TInt aCount ) + { + while( aArray.Count() < aCount ) + { + TInt err = aArray.Append( KInvalidRuleID ); + if ( err != KErrNone ) + { + return( err ); + } + } + + return KErrNone; + }