--- /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 <badesca.h>
+
+#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<TUint32>& 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<CNssSpeechItem>;
+ 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<CNssTag> *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<CNssTag>( 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<TUint32>;
+ if( !iDeleteRuleIDArray )
+ {
+ return KErrNoMemory;
+ }
+ // no deletion. iDeleteRuleIDArray might still be used
+ }
+// delete iDeleteRuleIDArray; // if any
+// iDeleteRuleIDArray = new RArray<TUint32>;
+ }
+ 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<CNssSpeechItem>*& anItemArray )
+ {
+ RUBY_DEBUG0( "CNssSpeechItemTrainer::HandleTrainError" );
+
+ TInt count = anItemArray->Count();
+
+ CleanUpTraining();
+
+ // Announce the error to the client
+ for( TInt k=0; k<count; k++ )
+ {
+ iTrainEventHandler->HandleTrainComplete( 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<TUint32>;
+
+ 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<RTrainingLanguageArray>& languages =
+ iTrainingParams->SindeLanguagesL() );
+
+ if ( errSinde == KErrNone )
+ {
+#ifdef __SINDE_TRAINING
+ TRAP_IGNORE(
+ const RArray<RTrainingLanguageArray>& 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<TUint32>;
+ if ( iRuleIDArray == 0 )
+ {
+ SendTrainingCallbacks( KErrNoMemory );
+ return;
+ }
+
+ RUBY_DEBUG1( "CNssSpeechItemTrainer::Retrain Calling AddVoiceTags (%d names)",
+ iPhraseArray.Count() );
+
+ TRAPD( errSinde, const RArray<RTrainingLanguageArray>& languages =
+ iTrainingParams->SindeLanguagesL() );
+
+ if ( errSinde == KErrNone )
+ {
+#ifdef __SINDE_TRAINING
+ TRAP_IGNORE(
+ const RArray<RTrainingLanguageArray>& 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<TNssSpeechItem> 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<CNssSpeechItem>; // 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<CNssSpeechItem>; // 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<TUint32> 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<TLanguage>& paramLangArray = aParams->Languages();
+
+ RArray<TLanguage>* langArray = new ( ELeave ) RArray<TLanguage>;
+ 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<TUint32>* 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<CNssSpeechItem>;
+ if ( !iSpeechItemBuffer )
+ {
+ return( EFalse );
+ }
+ }
+
+ return( ETrue );
+ }
+
+// ---------------------------------------------------------
+// CNssSpeechItemTrainer::SpeechItemBufferNeeded
+// Trys to allocate a grammar id buffer
+// ---------------------------------------------------------
+//
+TBool CNssSpeechItemTrainer::GrammarIdBufferNeeded()
+ {
+ if ( !iGrammarIdBuffer )
+ {
+ iGrammarIdBuffer = new RArray<TUint32>;
+ 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<CNssSpeechItem>& aSpeechItems,
+ RPointerArray<MDesCArray>& aPhrases )
+ {
+ // iPhraseArray is an array of arrays:
+ //
+ // |-- firstname1
+ // tag1 -|-- lastname1
+ //
+ // tag2 -|-- firstname2
+ // |-- lastname2
+
+ // Create training parameters, if none were given
+ if ( iTrainingParams == 0 )
+ {
+ RArray<TLanguage>* languages = new RArray<TLanguage>();
+
+ 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<RTrainingLanguageArray>& 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<TUint32>& aArray, TInt aCount )
+ {
+ while( aArray.Count() < aCount )
+ {
+ TInt err = aArray.Append( KInvalidRuleID );
+ if ( err != KErrNone )
+ {
+ return( err );
+ }
+ }
+
+ return KErrNone;
+ }