/*******************************************************************************
* AlloOps.cpp *
*-------------*
*   Description:
*       This module is the implementation file for the CAlloOps class.
*-------------------------------------------------------------------------------
*  Created By: mc                                        Date: 03/12/99
*  Copyright (C) 1999 Microsoft Corporation
*  All Rights Reserved
*
*******************************************************************************/

//--- Additional includes
#include "stdafx.h"

#ifndef __spttseng_h__
#include "spttseng.h"
#endif
#ifndef SPDebug_h
#include <spdebug.h>
#endif
#ifndef FeedChain_H
#include "FeedChain.h"
#endif
#ifndef Frontend_H
#include "Frontend.h"
#endif
#ifndef AlloOps_H
#include "AlloOps.h"
#endif




//-----------------------------
// Data.cpp
//-----------------------------
extern const unsigned short  g_Opcode_To_ASCII[];
extern const unsigned long   g_AlloFlags[];


/*****************************************************************************
* CBookmarkList::~CBookmarkList *
*-------------------------------*
*   Description:
*   Destructor for CBookmarkList
*       
********************************************************************** MC ***/
CBookmarkList::~CBookmarkList()
{
    SPDBG_FUNC( "CBookmarkList::~CBookmarkList" );
    BOOKMARK_ITEM*  pItem;

    //----------------------------------------
    //   Remove every item in link list.
    //----------------------------------------
    while( !m_BMList.IsEmpty() )
    {
        pItem = (BOOKMARK_ITEM*)m_BMList.RemoveHead();
        delete pItem;
    }
} /* CBookmarkList::~CBookmarkList */




/*****************************************************************************
* CFEToken::CFEToken *
*------------------------*
*   Description:
*   Initializer for CFEToken
*       
********************************************************************** MC ***/
CFEToken::CFEToken()
{
    SPDBG_FUNC( "CFEToken::CFEToken" );

    user_Volume = DEFAULT_USER_VOL;
    user_Rate = DEFAULT_USER_RATE;
    user_Pitch = DEFAULT_USER_PITCH;
    user_Emph = DEFAULT_USER_EMPH;
    user_Break = 0;
    pBMObj = NULL;

    memset( &tokStr[0], 0, sizeof(WCHAR) * TOKEN_LEN_MAX);
    tokLen = 0;
    memset( &phon_Str[0], 0, sizeof(short) * SP_MAX_PRON_LENGTH);
    phon_Len = 0;
    m_posClass = POS_UNK;
    POScode = MS_Unknown;
    m_TuneBoundaryType = NULL_BOUNDARY;
    m_Accent = K_NOACC;
    m_Boundary = K_NOBND;

	m_TermSil			= 0;
    m_DurScale			= 0.0f;
    m_ProsodyDurScale	= 1.0f;
	m_PitchBaseOffs		= 0.0f;
	m_PitchRangeScale	= 1.0f;

	// The following don't need to be init'd
    m_PronType			= PRON_LTS;
    sentencePosition	= 0;				// Source sentence position for this token
    sentenceLen			= 0; 				// Source sentence length for this token
    srcPosition			= 0;				// Source position for this token
    srcLen				= 0; 				// Source length for this token
    m_Accent_Prom		= 0;                // prominence prosodic control
    m_Boundary_Prom		= 0;                // prominence prosodic control
	m_TermSil			= 0;				// Pad word with silence (in sec)

	//--- Diagnostic
	m_AccentSource		= ACC_NoSource;
	m_BoundarySource	= BND_NoSource;
	m_SilenceSource		= SIL_NoSource;


} /* CFEToken::CFEToken */


/*****************************************************************************
* CFEToken::~CFEToken *
*-----------------------*
*   Description:
*   Destructor for CFEToken
*       
********************************************************************** MC ***/
CFEToken::~CFEToken()
{
    SPDBG_FUNC( "CFEToken::~CFEToken" );

    if( pBMObj != NULL )
    {
        //---------------------------------------
        // Dispose bookmark list
        //---------------------------------------
        delete pBMObj;
    }

} /* CFEToken::~CFEToken */





/*****************************************************************************
* CAlloCell::CAlloCell *
*------------------------*
*   Description:
*   Initializer for CAlloCell
*       
********************************************************************** MC ***/
CAlloCell::CAlloCell()
{
    SPDBG_FUNC( "CAlloCell::CAlloCell" );
    long    i;

    m_allo				= _SIL_;
    m_dur				= 0;
    m_ftDuration		= m_UnitDur = 0;
    m_knots				= KNOTS_PER_PHON;
    m_ctrlFlags			= 0;
    m_user_Rate			= 0;
    m_user_Volume		= DEFAULT_USER_VOL;
    m_user_Pitch		= 0;
    m_user_Emph			= 0;
    m_user_Break		= 0;
    m_Sil_Break			= 0;
    m_Pitch_HI			= 0;
    m_Pitch_LO			= 0;
    m_pBMObj			= NULL;
    m_ToBI_Boundary		= K_NOBND;
    m_ToBI_Accent		= K_NOACC;
	m_TuneBoundaryType	= m_NextTuneBoundaryType = NULL_BOUNDARY;
    m_DurScale			= 1.0;
    m_ProsodyDurScale	= 1.0;
	m_PitchBaseOffs		= 0.0f;
	m_PitchRangeScale	= 1.0f;
    for( i = 0; i < KNOTS_PER_PHON; i++ )
    {
        m_ftTime[i] = 0;
        m_ftPitch[i] = 100;
    }


    m_Accent_Prom	 = 0;                   // prominence prosodic control
    m_Boundary_Prom	 = 0;                 // prominence prosodic control
    m_PitchBufStart	 = 0;
    m_PitchBufEnd	 = 0;
    m_SrcPosition	 = 0;
    m_SrcLen		 = 0;
    m_SentencePosition	 = 0;
    m_SentenceLen		 = 0;

	//--- Diagnostic
	m_AccentSource		= ACC_NoSource;
	m_BoundarySource	= BND_NoSource;
	m_SilenceSource		= SIL_NoSource;
	m_pTextStr			= NULL;

} /* CAlloCell::CAlloCell */


/*****************************************************************************
* CAlloCell::~CAlloCell *
*-----------------------*
*   Description:
*   Destructor for CAlloCell
*       
********************************************************************** MC ***/
CAlloCell::~CAlloCell()
{
    SPDBG_FUNC( "CAlloCell::~CAlloCell" );

    if( m_pBMObj != NULL )
    {
        //---------------------------------------
        // Dispose bookmark list
        //---------------------------------------
        delete m_pBMObj;
    }

    if( m_pTextStr != NULL )
    {
        //---------------------------------------
        // Dispose bookmark list
        //---------------------------------------
        delete m_pTextStr;
    }


} /* CAlloCell::~CAlloCell */





/*****************************************************************************
* CAlloList::CAlloList *
*------------------------*
*   Description:
*   Initialize list with 2 silence entries. These will 
*   become the head an tail when real entries are stuffed
*       
********************************************************************** MC ***/
CAlloList::CAlloList()
{
    SPDBG_FUNC( "CAlloList::CAlloList" );
    CAlloCell   *pCell;

    m_cAllos = 0;
	m_ListPos = NULL;
    //------------------------------------
    // Create initial TAIL silence cell
    //------------------------------------
    pCell = new CAlloCell;
    if( pCell )
    {
        m_AlloCellList.AddHead( pCell );
        pCell->m_ctrlFlags |= WORD_START + TERM_BOUND;
        pCell->m_TuneBoundaryType = TAIL_BOUNDARY;
		pCell->m_SilenceSource = SIL_Tail;
        m_cAllos++;
    }
    //------------------------------------
    // Create initial HEAD silence cell
    //------------------------------------
    pCell = new CAlloCell;
    if( pCell )
    {
        m_AlloCellList.AddHead( pCell );
        pCell->m_ctrlFlags |= WORD_START;
		pCell->m_SilenceSource = SIL_Head;
        m_cAllos++;
    }
} /* CAlloList::CAlloList */


                


/*****************************************************************************
* CAlloList::~CAlloList *
*-------------------------*
*   Description:
*   Remove every item in link list.
*       
********************************************************************** MC ***/
CAlloList::~CAlloList()
{
    SPDBG_FUNC( "CAlloList::~CAlloList" );
    CAlloCell   *pCell;

    while( !m_AlloCellList.IsEmpty() )
    {
        pCell = (CAlloCell*)m_AlloCellList.RemoveHead();
        delete pCell;
    }

} /* CAlloList::~CAlloList */





/*****************************************************************************
* CAlloList::GetAllo *
*---------------------*
*   Description:
*   Return pointer of allocell at index
*       
********************************************************************** MC ***/
CAlloCell *CAlloList::GetCell( long index )
{
    SPDBG_FUNC( "CAlloList::GetCell" );

    return (CAlloCell*)m_AlloCellList.GetAt( m_AlloCellList.FindIndex( index ));
} /* CAlloList::GetCell */


/*****************************************************************************
* CAlloList::GetTailCell *
*-------------------------*
*   Description:
*   Return pointer of last allo in link list
*       
********************************************************************** MC ***/
CAlloCell *CAlloList::GetTailCell()
{
    SPDBG_FUNC( "CAlloList::GetTailCell" );

    return (CAlloCell*)m_AlloCellList.GetTail();
} /* CAlloList::GetTailCell */


/*****************************************************************************
* CAlloList::GetTailCell *
*-----------------------*
*   Description:
*   Return allo list size
*       
********************************************************************** MC ***/
long CAlloList::GetCount()
{
    SPDBG_FUNC( "CAlloList::GetCount" );

    return m_AlloCellList.GetCount();
} /* CAlloList::GetCount */





/*****************************************************************************
* PrintPhon *
*-----------*
*   Description:
*   Print 2-char allo name
*       
********************************************************************** MC ***/
void PrintPhon( ALLO_CODE allo, char * /*msgStr*/)
{
    SPDBG_FUNC( "PrintPhon" );

    unsigned short  nChar;
    
    nChar = g_Opcode_To_ASCII[allo];
    if( nChar >> 8 )
    {
        SPDBG_DMSG1( "%c", nChar >> 8 );
    }
    if( nChar && 0xFF )
    {
        SPDBG_DMSG1( "%c", nChar & 0xFF );
    }
} /* PrintPhon */




/*****************************************************************************
* CAlloList::OutAllos *
*--------------------*
*   Description:
*   Dump ALLO_CELL contents
*       
********************************************************************** MC ***/
void CAlloList::OutAllos()
{
    SPDBG_FUNC( "CAlloOps::OutAllos" );
    CAlloCell       *pCurCell;

    long    i, flags, flagsT;
    char    msgStr[400];
    
    for( i = 0; i < m_cAllos; i++ )
    {
        pCurCell = GetCell( i );
        flags = pCurCell->m_ctrlFlags;
        
        if( flags & WORD_START)
        {
            SPDBG_DMSG0( "\n" );
        }
        
        //----------------------------
        // Allo
        //----------------------------
        PrintPhon( pCurCell->m_allo, msgStr );
        
        //----------------------------
        // Duration
        //----------------------------
        SPDBG_DMSG1( "\t%.3f\t", pCurCell->m_ftDuration );
        
        //----------------------------
        // Boundry
        //----------------------------
        if( flags & BOUNDARY_TYPE_FIELD)
        {
            SPDBG_DMSG0( "(" );
            if( flags & WORD_START)
            {
                SPDBG_DMSG0( "-wS" );
            }
            if( flags & TERM_BOUND)
            {
                SPDBG_DMSG0( "-tB" );
            }
            SPDBG_DMSG0( ")\t" );
        }
        
        //----------------------------
        // Syllable type
        //----------------------------
        if( flags & SYLLABLE_TYPE_FIELD)
        {
            SPDBG_DMSG0( "(" );
            if( flags & WORD_END_SYLL)
            {
                SPDBG_DMSG0( "-wE" );
            }
            if( flags & TERM_END_SYLL)
            {
                SPDBG_DMSG0( "-tE" );
            }
            SPDBG_DMSG0( ")\t" );
        }
        
        //----------------------------
        // Syllable order
        //----------------------------
        if( flags & SYLLABLE_ORDER_FIELD)
        {
            SPDBG_DMSG0( "(" );
            
            flagsT = flags & SYLLABLE_ORDER_FIELD;
            if( flagsT == FIRST_SYLLABLE_IN_WORD)
            {
                SPDBG_DMSG0( "-Fs" );
            }
            else if( flagsT == MID_SYLLABLE_IN_WORD)
            {
                SPDBG_DMSG0( "-Ms" );
            }
            else if( flagsT == LAST_SYLLABLE_IN_WORD)
            {
                SPDBG_DMSG0( "-Ls" );
            }
            SPDBG_DMSG0( ")\t" );
        }
        
        //----------------------------
        // Stress
        //----------------------------
        if( flags & PRIMARY_STRESS)
        {
            SPDBG_DMSG0( "-Stress\t" );
        }
        
        //----------------------------
        // Word initial consonant
        //----------------------------
        if( flags & WORD_INITIAL_CONSONANT)
        {
            SPDBG_DMSG0( "-InitialK\t" );
        }
        
        //----------------------------
        // Syllable start
        //----------------------------
        if( flags & SYLLABLE_START)
        {
            SPDBG_DMSG0( "-Syll\t" );
        }
        
        SPDBG_DMSG0( "\n" );
        }
} /* CAlloList::OutAllos */




/*****************************************************************************
* CAlloList::WordToAllo *
*-----------------------*
*   Description:
*   Copy word token to AlloCells
*   Insert allos BEFORE 'pEndCell'
*       
********************************************************************** MC ***/
bool CAlloList::WordToAllo( CFEToken *pPrevTok, CFEToken *pTok, CFEToken *pNextTok, CAlloCell *pEndCell )
{
    SPDBG_FUNC( "CAlloList::WordToAllo" );

    long    i;
    long    startLatch;
    CAlloCell   *pCurCell;
    long    firstVowel, lastVoiced;
    bool    gotAccent, isStressed;
	bool	hasSpeech;
    
    //-----------------------------------------
    // First, find ToBI accent locations
    //-----------------------------------------
    firstVowel  = lastVoiced = (-1);
    gotAccent   = false;
	hasSpeech	= false;
    for( i = 0; i < pTok->phon_Len; i++ )
    {
        isStressed = false;
        if( pTok->phon_Str[i] < _STRESS1_ )
        {
            //----------------------------
            // Potential ToBI accent
            //----------------------------
            if( (!gotAccent) && (g_AlloFlags[pTok->phon_Str[i]] & KVOWELF) )
            {
                if( (i < (pTok->phon_Len -1)) && (pTok->phon_Str[i+1] == _STRESS1_) )
                {
                    //-------------------------------------
                    // Put accent at 1st stressed vowel
                    //-------------------------------------
                    firstVowel = i;
                    gotAccent = true;
                }
                else if( firstVowel < 0 )
                {
                    //-------------------------------------
                    // In case there's no stressed vowel 
                    // in this word, use 1st vowel
                    //-------------------------------------
                    firstVowel = i;
                }
            }
            //----------------------------
            // Potential ToBI boundary
            //----------------------------
            if( g_AlloFlags[pTok->phon_Str[i]] & KVOICEDF )
            {
                lastVoiced = i;
            }
        }
    }
    //-----------------------------------------
    // Now, copy data to allo list
    //-----------------------------------------
    startLatch  = true;
    for( i = 0; i < pTok->phon_Len; i++ )
    {
        if( pTok->phon_Str[i] < _STRESS1_ )
        {
			if( (pTok->phon_Str[i] == _SIL_) && (pTok->m_TuneBoundaryType >= SUB_BOUNDARY_1) )
			{
				//----------------------------------------------------------------
				// Before skipping this, propagate the dur scale gain
				//----------------------------------------------------------------
				if( pTok->m_DurScale == 0 )
				{
					if( pPrevTok )
					{
						pTok->m_DurScale = pPrevTok->m_DurScale;
					}
					else
					{
						pTok->m_DurScale = 1.0;
					}
				}
				continue;
			}
            //------------------------------------
            // Create new cell
            //------------------------------------
            pCurCell = new CAlloCell;
            if( pCurCell )
            {
                m_AlloCellList.InsertBefore( m_AlloCellList.Find(pEndCell), pCurCell);
                m_cAllos++;

                //----------------------------
                // Copy only phons
                //----------------------------
                pCurCell->m_allo = (ALLO_CODE) pTok->phon_Str[i];
                //---------------------------------------------
                // See if this allo will generate speech
                //---------------------------------------------
				if( (pCurCell->m_allo >= _IY_) &&
					(pCurCell->m_allo <= _DX_) &&
					(pCurCell->m_allo != _SIL_) )
				{
					hasSpeech = true;
				}

                //----------------------------
                // Save src position
                //----------------------------
                pCurCell->m_SrcPosition = pTok->srcPosition;
                pCurCell->m_SrcLen = pTok->srcLen;
                pCurCell->m_SentencePosition = pTok->sentencePosition;
                pCurCell->m_SentenceLen = pTok->sentenceLen;

                //----------------------------
                // Flag WORD START?
                //----------------------------
                if( startLatch )
                {
                    pCurCell->m_ctrlFlags |= WORD_START;
                    startLatch = false;
                }

                //----------------------------
                // Is next allo a STRESS?
                //----------------------------
                if( i < (pTok->phon_Len -1) )
                {
                    if( pTok->phon_Str[i+1] == _STRESS1_ )
                    {
                        pCurCell->m_ctrlFlags |= PRIMARY_STRESS;
                    }
					else
					{
						//----------------------------------------------
						// Voice inventory does not have unstressed
						// entries for these diphongs
						//----------------------------------------------
						if( (pCurCell->m_allo == _AW_) ||
							(pCurCell->m_allo == _AY_) ||
							(pCurCell->m_allo == _EY_) ||
							(pCurCell->m_allo == _OY_) )
						{
							pCurCell->m_ctrlFlags |= PRIMARY_STRESS;
						}
					}
                }

				//---------------------------
				// Diagnostic
				//---------------------------
				if( pCurCell->m_allo == _SIL_ )
				{
					pCurCell->m_SilenceSource = pTok->m_SilenceSource;
				}
                //----------------------------
                // Place ToBI accent
                //----------------------------
                if( i == firstVowel )
                {
                    pCurCell->m_ToBI_Accent = pTok->m_Accent;
					//---------------------------
					// Diagnostic
					//---------------------------
					pCurCell->m_AccentSource = pTok->m_AccentSource;
					pCurCell->m_pTextStr = new char[pTok->tokLen+1];
					if( pCurCell->m_pTextStr )
					{
						WideCharToMultiByte (	CP_ACP, 0, 
												pTok->tokStr, -1, 
												pCurCell->m_pTextStr, pTok->tokLen+1, 
												NULL, NULL);
					}
                }
                pCurCell->m_Accent_Prom = pTok->m_Accent_Prom;
                //----------------------------
                // Place ToBI boundary
                //----------------------------
                if( i == lastVoiced )
                {
                    pCurCell->m_ToBI_Boundary = pTok->m_Boundary;
					//---------------------------
					// Diagnostic
					//---------------------------
					pCurCell->m_BoundarySource = pTok->m_BoundarySource;
                }
                pCurCell->m_Boundary_Prom = pTok->m_Boundary_Prom;

                //----------------------------
                // User Controls
                //----------------------------
                pCurCell->m_user_Volume = pTok->user_Volume;
                pCurCell->m_user_Rate = pTok->user_Rate;
                pCurCell->m_user_Pitch = pTok->user_Pitch;
				pCurCell->m_user_Emph = 0;
				if( pTok->user_Emph > 0 )
				{
					if( i == firstVowel )
					{
						pCurCell->m_user_Emph = pTok->user_Emph;
						pCurCell->m_ctrlFlags |= PRIMARY_STRESS;
					}
				}
                pCurCell->m_user_Break = pTok->user_Break;
                pCurCell->m_pBMObj = pTok->pBMObj;
                pTok->pBMObj = NULL;

				//-----------------------------------------------
				// If token's m_DurScale is not defined,
				//  try to use prev token's ratio
				//-----------------------------------------------
				if( pTok->m_DurScale == 0 )
				{
					if( pPrevTok )
					{
						pCurCell->m_DurScale = pPrevTok->m_DurScale;
					}
					else
					{
						pCurCell->m_DurScale = 1.0;
					}
					//-------------------------------------------------------
					// Write back in case next token is also undefined
					//-------------------------------------------------------
					pTok->m_DurScale = pCurCell->m_DurScale;
				}
				else
				{
					pCurCell->m_DurScale = pTok->m_DurScale;
				}
				pCurCell->m_ProsodyDurScale = pTok->m_ProsodyDurScale;

				if( pNextTok )
				{
					pCurCell->m_NextTuneBoundaryType = pNextTok->m_TuneBoundaryType;
				}
				else
				{
					pCurCell->m_NextTuneBoundaryType = NULL_BOUNDARY;
				}
				pCurCell->m_PitchBaseOffs = pTok->m_PitchBaseOffs;
				pCurCell->m_PitchRangeScale = pTok->m_PitchRangeScale;

                //----------------------------------------------
                // Is this a term word?
                //----------------------------------------------
                pCurCell->m_TuneBoundaryType = pTok->m_TuneBoundaryType;
                if( pTok->m_TuneBoundaryType != NULL_BOUNDARY )
                {
                    pCurCell->m_ctrlFlags |= TERM_BOUND + WORD_START;
                }
            }
        }

    }
	//----------------------------------------
	// Insert word pause?
	//----------------------------------------
	if( pTok->m_TermSil > 0 )
	{
        pCurCell = new CAlloCell;
        if( pCurCell )
        {
            m_AlloCellList.InsertBefore( m_AlloCellList.Find(pEndCell), pCurCell);
            m_cAllos++;

            //----------------------------
            // Add silence
            //----------------------------
            pCurCell->m_allo = _SIL_;

            //----------------------------
            // Save src position
            //----------------------------
            pCurCell->m_SrcPosition = pTok->srcPosition;
            pCurCell->m_SrcLen = pTok->srcLen;
            pCurCell->m_SentencePosition = pTok->sentencePosition;
            pCurCell->m_SentenceLen = pTok->sentenceLen;
            //----------------------------
            // User Controls
            //----------------------------
            pCurCell->m_user_Volume = pTok->user_Volume;
            pCurCell->m_user_Rate = pTok->user_Rate;
            pCurCell->m_user_Pitch = pTok->user_Pitch;
            pCurCell->m_user_Emph = pTok->user_Emph;
            pCurCell->m_user_Break = pTok->user_Break;
            pCurCell->m_pBMObj = NULL;
            pCurCell->m_TuneBoundaryType = pTok->m_TuneBoundaryType;
            pCurCell->m_Boundary_Prom = pTok->m_Boundary_Prom;
            pCurCell->m_Accent_Prom = pTok->m_Accent_Prom;
			pCurCell->m_ctrlFlags = 0;
			pCurCell->m_UnitDur = pTok->m_TermSil;
            pCurCell->m_Sil_Break = (unsigned long)(pCurCell->m_UnitDur * 1000);	// sec -> ms
			pCurCell->m_user_Break = 0;
			pCurCell->m_DurScale = pTok->m_DurScale;
			pCurCell->m_ProsodyDurScale = 1.0f;
		}
	}

	return hasSpeech;
} /* CAlloList::WordToAllo */