/****************************************************************************
*
*   BasicTTS.cpp
*
*   Sample text-to-speech program that demonstrates the basics of using the
*   SAPI 5.0 TTS APIs.
*
*   Copyright (c) 1999 Microsoft Corporation All Rights Reserved.
*
*****************************************************************************/

//--- Includes --------------------------------------------------------------

#include <windows.h>
#include <stdio.h>
#include <atlbase.h>
#include <shellapi.h>
#include <sapi.h>

//--- Const Defines----------------------------------------------------------

const static WCHAR g_szMarkupDemo[] =
    L"<VOICE OPTIONAL=\"Female\">Tags can be used to change attributes such as\n"
    L"    <VOICE OPTIONAL=\"Gender=Male;Volume=Loud\">the voice font that is used,</VOICE>\n"
    L"    the <VOLUME LEVEL=\"65\">volume of the voice</VOLUME>\n"
    L"    and other attributes of speech such as pitch and rate.\n"
    L"</VOICE>\n\n";


/****************************************************************************
* MixWithWav *
*------------*
*   Description:
*       Plays an introductory audio file followed by synthetic speech.
*
*   Returns:
*       HRESULT from Speak call or
*       S_FALSE if the Intro.Wav file is not present
*
*****************************************************************************/

HRESULT MixWithWav(ISpVoice * pVoice)
{
    HRESULT hr = S_OK;
#ifndef _WIN32_WCE
    wprintf(L"Now a sample of mixed wave files and generated speech.\n\n");
#else
    OutputDebugString(L"Now a sample of mixed wave files and generated speech.\n\n");
#endif
    hr = pVoice->Speak(L"Intro.Wav", SPF_IS_FILENAME | SPF_ASYNC, NULL);
    if (hr == STG_E_FILENOTFOUND)
    {
#ifndef _WIN32_WCE
        wprintf(L"ERROR:  Can not find file Intro.Wav.  Wave file mixing sample will be skipped.\n\n");
#else
        OutputDebugStringW(L"ERROR:  Can not find file Intro.Wav.  Wave file mixing sample will be skipped.\n\n");
#endif
        hr = S_FALSE;   // Return success code so sample will continue...
    }
    else
    {
        if (SUCCEEDED(hr))
        {
            hr = pVoice->Speak(L"<VOICE OPTIONAL=\"Male\">one hundred eighty dollars and twenty seven cents.</VOICE>", 0, NULL );
        }
    }
    return hr;
}

/****************************************************************************
* CreateSampleFile *
*------------------*
*   Description:
*       Creates a file named Created.Wav using a TTS voice and then starts
*   the currently registered audio playback application.
*
*   Returns:
*       HRESULT
*
*****************************************************************************/

HRESULT CreateSampleFile(ISpVoice * pVoice)
{
    HRESULT hr = S_OK;
    CComPtr<ISpStream> cpWavStream;
    WAVEFORMATEX wfex;
    wfex.wFormatTag = WAVE_FORMAT_PCM;
    wfex.nChannels = 1;
    wfex.nSamplesPerSec = 22050;
    wfex.nAvgBytesPerSec = 22050 * 2;
    wfex.nBlockAlign = 2;
    wfex.wBitsPerSample = 16;
    wfex.cbSize = 0;
    hr = cpWavStream.CoCreateInstance(CLSID_SpStream);
    if (SUCCEEDED(hr))
    {
        hr = cpWavStream->BindToFile(L"Created.Wav", SPFM_CREATE_ALWAYS, &SPDFID_WaveFormatEx, &wfex, 0);
    }
    if (SUCCEEDED(hr))
    {
        hr = pVoice->SetOutput(cpWavStream, TRUE);
    }
    if (SUCCEEDED(hr))
    {
        hr = pVoice->Speak( L"This audio file was created using SAPI five text to speech.", 0, NULL);
        pVoice->SetOutput(NULL, TRUE);
    }
    if (SUCCEEDED(hr))
    {
        hr = cpWavStream->Close();
    }
    if (SUCCEEDED(hr))
    {
#ifndef _WIN32_WCE
        printf("Now I'll start the media player on the created file...");
#else
        OutputDebugString(L"Now I'll start the media player on the created file...");
#endif
        pVoice->Speak( L"Press the play button to play the recorded audio.", 0, NULL);
#ifndef _WIN32_WCE
        ::ShellExecute(NULL, "open", _T("Created.Wav"), NULL, NULL, SW_SHOWNORMAL);
#endif
    }
    return hr;
}

/****************************************************************************
* SpeakWithEvents *
*-----------------*
*   Description:
*       Speaks the provided text string and displays the words when the
*   they are spoken.
*
*   Returns:
*       HRESULT
*
*****************************************************************************/

HRESULT SpeakWithEvents(ISpVoice * pVoice, const WCHAR * psz)
{
    HRESULT hr = S_OK;
    hr = pVoice->SetInterest(SPFEI(SPEI_WORD_BOUNDARY) | SPFEI(SPEI_END_INPUT_STREAM), 0);
    if (SUCCEEDED(hr))
    {
        hr = pVoice->Speak(psz, SPF_ASYNC, NULL);
    }
    ULONG i = 0;
    bool fDone = false;
    while (SUCCEEDED(hr) && (!fDone))
    {
        hr = pVoice->WaitForNotifyEvent(INFINITE);
        if (SUCCEEDED(hr))
        {
            SPVOICESTATUS Stat;
            hr = pVoice->GetStatus(&Stat, NULL);
            if (SUCCEEDED(hr) && (Stat.dwRunningState & SPRS_DONE) == 0)
            {
                while (i < Stat.ulInputWordPos + Stat.ulInputWordLen)
                {
#ifndef _WIN32_WCE
                    putwchar(psz[i++]);
#else
                    WCHAR wsz[2];
                    wsz[0] = psz[i++];
                    wsz[1] = 0;
                    OutputDebugStringW(wsz);
#endif
                }
            }
            else
            {
#ifndef _WIN32_WCE
                wprintf(L"%s\n\n", psz + i);
#else
                RETAILMSG(TRUE,(L"%s\n\n", psz + i));
#endif
                fDone = true;
            }
        }
    }
    return hr;
}

/****************************************************************************
* main *
*------*
*   Description:
*       Entry point for sample program
*
*   Returns:
*       HRESULT
*
*****************************************************************************/

int _tmain(int argc, TCHAR* argv[])
{
    HRESULT hr;
#ifndef _WIN32_WCE
    wprintf(L"SAPI 5.0 Sample TTS Application\n\n");
#else
    OutputDebugString(L"SAPI 5.0 Sample TTS Application\n\n");
#endif
    hr = ::CoInitialize(NULL);
    if(SUCCEEDED(hr))
    {
        CComPtr<ISpVoice> cpVoice;
        hr = cpVoice.CoCreateInstance(CLSID_SpVoice);
        if (SUCCEEDED(hr))
        {
            hr = cpVoice->Speak(L"This sample program uses basic text to speech operations.", 0, NULL);
        }
        if (SUCCEEDED(hr))
        {
#ifndef _WIN32_WCE
            wprintf(g_szMarkupDemo);
#else
            OutputDebugString(g_szMarkupDemo);
#endif
            hr = cpVoice->Speak(g_szMarkupDemo, 0, NULL);
        }
        if (SUCCEEDED(hr))
        {
            hr = SpeakWithEvents(cpVoice, L"This is a demonstration of how words can be displayed when they are spoken.");
        }
        if (SUCCEEDED(hr))
        {
            hr = MixWithWav(cpVoice);
        }
        if (SUCCEEDED(hr))
        {
            hr = CreateSampleFile(cpVoice);
        }
        cpVoice.Release();  // Must release prior to CoUninitialize or we'll GP Fault
        CoUninitialize();
    }
    if (FAILED(hr))
    {
#ifndef _WIN32_WCE
        wprintf(L"Sample program failed with error code 0x%x\n", hr);
#else
        RETAILMSG(TRUE,(L"Sample program failed with error code 0x%x\n", hr));
#endif
    }
	return hr;
}