/*************************************************************************
	Project:    Babble
    Module:     TTS.C

    Author:     Charles Oppermann
    Date:       28 September 1996
    
    Notes:      Text to Speech support routines.  Finds a TTS engine and
                initializes it.

    Copyright (C) 1996 by Microsoft Corporation.  All rights reserved.
    See bottom of file for disclaimer
    
    History:

*************************************************************************/
//#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <string.h>
#include <stdio.h>
#include <mmsystem.h>
#include <initguid.h>
#include <objbase.h>
#include <objerror.h>
#include <ole2ver.h>

// Bring in Speech API declarations
#include <speech.h>

// Bring in our own project stuff
#include "tts.h"
#include "resource.h"

// Local functions
PITTSCENTRAL FindAndSelect(PTTSMODEINFO pTTSInfo);

// Variables
PITTSCENTRAL        g_pITTSCentral;
PITTSBUFNOTIFYSINK  g_pITTSBufNotifySink;
CHAR                g_szLastStringSpoken[1024] = { NULL };

/*************************************************************************
    Function:   SpeakString
    Purpose:    Sends a string of text to the speech engine
    Inputs:     PSZ pszSpeakText - String of ANSI characters to be spoken
                                   Speech control tags can be embedded.
    Returns:    BOOL - TRUE if string was buffered correctly
    History:
*************************************************************************/
BOOL SpeakString(PSZ pszSpeakText)
{
    HRESULT hRes;
    SDATA   sData;

    sData.dwSize = lstrlen(pszSpeakText);
    sData.pData = pszSpeakText;

    // Check for redundent speak, filter out
    if (lstrcmp(pszSpeakText, (LPCSTR)&g_szLastStringSpoken) == 0)
        return(FALSE);

    // Different string, save off
    lstrcpy((LPTSTR)&g_szLastStringSpoken, pszSpeakText);

    // Interrupt speech
    g_pITTSCentral->AudioReset();

    // Put the text into the egine's speaking queue, and give
    // the engine the address of the ITTSBufNotifySink interface for
    // buffer-related notifications.
    hRes = g_pITTSCentral->TextData(CHARSET_TEXT, 
                                    TTSDATAFLAG_TAGGED, // Indicate that there might be control tags
                                    sData,
                                    NULL, // g_pIBufNotifySink,  Will add later
                                    IID_ITTSBufNotifySink);

    // Check to see if the call succeeded
    if (FAILED(hRes))
        return(FALSE);
    else
        return(TRUE);
}

/*************************************************************************
    Function:   InitTTS
    Purpose:    Starts the Text to Speech Engine
    Inputs:     none
    Returns:    BOOL - TRUE if successful
    History:
*************************************************************************/
BOOL InitTTS(void)
{
    TTSMODEINFO   ttsModeInfo;
    
    // Attempt to initialize OLE
    if (FAILED(CoInitialize(NULL))) 
        return FALSE;

    // Zero the ttsModeInfo stucture (we depend on some fields being zero)
    ZeroMemory(&ttsModeInfo, sizeof(ttsModeInfo));
    
    // Put in any other parameters into ttsModeInfo here
    lstrcpy(ttsModeInfo.szMfgName, "Centigram");
    
    // Find a engine based on the ttsModeInfo
    g_pITTSCentral = FindAndSelect(&ttsModeInfo);
    
    // Did we get a interface to an engine?
    if (!g_pITTSCentral)
        {
        // We did not, present a error message
        MessageBox (NULL, "Could not find a Text to Speech engine.", NULL, MB_OK);
        return(FALSE);
        }

    return(TRUE);
}

/*************************************************************************
    Function:   UnInitTTS
    Purpose:    Shuts down the Text to Speech subsystem
    Inputs:     none
    Returns:    BOOL - TRUE if successful
    History:
*************************************************************************/
BOOL UnInitTTS(void)
{
    // Release out TTS object - if we have one
    if (g_pITTSCentral)
        g_pITTSCentral->Release();

    // Shut down OLE
    CoUninitialize();
       
    return(TRUE);
}

/*************************************************************************
    Function:   FindAndSelect
    Purpose:    Finds and selects the TTS engine
    Inputs:     PTTSMODEINFO pTTSInfo - Desired mode
    Returns:    PITTSCENTRAL - Pointer to ITTSCentral interface of engine
    History:
*************************************************************************/
PITTSCENTRAL FindAndSelect(PTTSMODEINFO pTTSInfo)
{
    HRESULT        hRes;
    TTSMODEINFO    ttsResult;        // final result
    WCHAR          Zero = 0;
    PITTSFIND      pITTSFind;             // find interface
    PIAUDIOMULTIMEDIADEVICE    pIAMM;      // multimedia device interface for audio-dest
    PITTSCENTRAL  pITTSCentral;           // central interface


    hRes = CoCreateInstance(CLSID_TTSEnumerator,
                            NULL,
                            CLSCTX_ALL,
                            IID_ITTSFind,
                            (void**)&pITTSFind);
    
    if (FAILED(hRes))
        return NULL;

    // Find a Engine for us
    hRes = pITTSFind->Find(pTTSInfo, NULL, &ttsResult);
    if (FAILED(hRes))
        {
        pITTSFind->Release();
        return NULL;     // error
        }

    // Get the audio dest
    hRes = CoCreateInstance(CLSID_MMAudioDest, 
                            NULL,
                            CLSCTX_ALL, 
                            IID_IAudioMultiMediaDevice, 
                            (void**)&pIAMM);
    if (FAILED(hRes))
        {
        pITTSFind->Release();
        return NULL;     // error
        }
	
    pIAMM->DeviceNumSet (WAVE_MAPPER);

	// Pass off the multi-media-device interface as an IUnknown (since it is one)
    hRes = pITTSFind->Select(   ttsResult.gModeID, 
                                &pITTSCentral, 
                                (LPUNKNOWN) pIAMM);

    if (FAILED(hRes))
        {
        pITTSFind->Release();
        return NULL;
        };

    // free random stuff up
    pITTSFind->Release();

    return pITTSCentral;
}

/*************************************************************************
    THE INFORMATION AND CODE PROVIDED HEREUNDER (COLLECTIVELY REFERRED TO
    AS "SOFTWARE") IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN
    NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR
    ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL,
    CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF
    MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR
    LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE
    FOREGOING LIMITATION MAY NOT APPLY.
*************************************************************************/