void CTTSApp::MainHandleCommand( int id, HWND hWndControl, UINT codeNotify )
/////////////////////////////////////////////////////////////////
//
// Handle each of the WM_COMMAND messages that come in, and deal with
// them appropriately
//
{
    UINT                cNumChar = 0;
    HRESULT             hr = S_OK;
    TCHAR               szAFileName[NORM_SIZE] = _T("");
    static BOOL         bIsUnicode = FALSE;
    BOOL                bWavFileOpened = FALSE;
    LRESULT             iFormat;
    CComPtr<ISpStream>  cpWavStream;
    CComPtr<ISpStreamFormat>    cpOldStream;
    HWND                hwndEdit;
    BOOL                bFileOpened = FALSE;

    // Get handle to the main edit box
    hwndEdit = GetDlgItem( m_hWnd, IDE_EDITBOX );

    switch(id)
    {
        // About Box display
        case IDC_ABOUT:
            ::DialogBox( m_hInst, (LPCTSTR)IDD_ABOUT, m_hWnd, (DLGPROC)About );
            break;

        // Any change to voices is sent to VoiceChange() function
        case IDC_COMBO_VOICES:
            if( codeNotify == CBN_SELCHANGE )
            {
                hr = VoiceChange();
            }

            if( FAILED( hr ) )
            {
                TTSAppStatusMessage( m_hWnd, _T("Error changing voices\r\n") );
            }

            break;

        // If user wants to speak a file pop the standard windows open file
        // dialog box and load the text into a global buffer (m_pszwFileText)
        // which will be used when the user hits speak.
        case IDB_OPEN:
            bFileOpened = CallOpenFileDialog( szAFileName,
                        _T("TXT (*.txt)\0*.txt\0XML (*.xml)\0*.xml\0All Files (*.*)\0*.*\0") );
            if( bFileOpened )
            {
                DWORD   dwFileSize = 0;
                
                wcscpy_s( m_szWFileName, _countof(m_szWFileName), CT2W( szAFileName ) );
                ReadTheFile( szAFileName, &bIsUnicode, &m_pszwFileText );
                
                if( bIsUnicode )
                {
                    // Unicode source
                    UpdateEditCtlW( m_pszwFileText );
                }
                else
                {
                    // MBCS source
#ifdef _UNICODE
                    LPTSTR pszFileText = _tcsdup( m_pszwFileText );
#else
                    // We're compiling ANSI, so we need to convert the string to MBCS
                    // Note that a W2T may not be good here, since this string might 
                    // be very big
                    LPTSTR pszFileText = NULL;
                    int iNeeded = ::WideCharToMultiByte( CP_ACP, 0, m_pszwFileText, -1, NULL, 0, NULL, NULL );
                    pszFileText = (LPTSTR) ::malloc( sizeof( TCHAR ) * ( iNeeded + 1 ) );
                    ::WideCharToMultiByte( CP_ACP, 0, m_pszwFileText, -1, pszFileText, iNeeded + 1, NULL, NULL );
#endif
                    if ( pszFileText )
                    {
                        SetDlgItemText( m_hWnd, IDE_EDITBOX, pszFileText );
                        free( pszFileText );
                    }

                }
            }
            else
            {
                wcscpy_s( m_szWFileName, _countof(m_szWFileName), L"" );
            }
            // Always SetFocus back to main edit window so text highlighting will work
            SetFocus( hwndEdit );
            break;
        
        // Handle speak
        case IDB_SPEAK:
            HandleSpeak();
            break;

        case IDB_PAUSE:
            if( !m_bStop )
            {
                if( !m_bPause )
                {
                    SetWindowText( GetDlgItem( m_hWnd, IDB_PAUSE ), _T("Resume") );
                    // Pause the voice...
                    m_cpVoice->Pause();
                    m_bPause = TRUE;
                    TTSAppStatusMessage( m_hWnd, _T("Pause\r\n") );
                }
                else
                {
                    SetWindowText( GetDlgItem( m_hWnd, IDB_PAUSE ), _T("Pause") );
                    m_cpVoice->Resume();
                    m_bPause = FALSE;
                }
            }
            SetFocus( hwndEdit );
            break;

        case IDB_STOP:
            TTSAppStatusMessage( m_hWnd, _T("Stop\r\n") );
            // Set the global audio state to stop
            Stop();
            SetFocus( hwndEdit );
            break;

        case IDB_SKIP:
            {
                SetFocus( hwndEdit );
                int fSuccess = false;
                int SkipNum = GetDlgItemInt( m_hWnd, IDC_SKIP_EDIT, &fSuccess, true );
                ULONG ulGarbage = 0;
                WCHAR szGarbage[] = L"Sentence";
                if ( fSuccess )
                {
                    TTSAppStatusMessage( m_hWnd, _T("Skip\r\n") );
                    m_cpVoice->Skip( szGarbage, SkipNum, &ulGarbage );
                }
                else
                {
                    TTSAppStatusMessage( m_hWnd, _T("Skip failed\r\n") );
                }
                break;
            }

        case IDE_EDITBOX:
            // Set the global audio state to stop if user has changed contents of edit control
            if( codeNotify == EN_CHANGE )
            {
                Stop();
            }
            break;

        case IDB_SPEAKWAV:
            bWavFileOpened = CallOpenFileDialog( szAFileName,
                         _T("WAV (*.wav)\0*.wav\0All Files (*.*)\0*.*\0") );
            // Speak the wav file using SpeakStream
            if( bWavFileOpened )
            {
                WCHAR                       szwWavFileName[NORM_SIZE] = L"";;

                wcscpy_s( szwWavFileName, _countof(szwWavFileName), CT2W( szAFileName ) );

                // User helper function found in sphelper.h to open the wav file and
                // get back an IStream pointer to pass to SpeakStream
                hr = SPBindToFile( szwWavFileName, SPFM_OPEN_READONLY, &cpWavStream );

                if( SUCCEEDED( hr ) )
                {
                    hr = m_cpVoice->SpeakStream( cpWavStream, SPF_ASYNC, NULL );
                }

                if( FAILED( hr ) )
                {
                    TTSAppStatusMessage( m_hWnd, _T("Speak error\r\n") );
                }
            }
            break;

        // Reset all values to defaults
        case IDB_RESET:
            TTSAppStatusMessage( m_hWnd, _T("Reset\r\n") );
            SendDlgItemMessage( m_hWnd, IDC_VOLUME_SLIDER, TBM_SETPOS, TRUE, m_DefaultVolume );
            SendDlgItemMessage( m_hWnd, IDC_RATE_SLIDER, TBM_SETPOS, TRUE, m_DefaultRate );
            SendDlgItemMessage( m_hWnd, IDC_SAVETOWAV, BM_SETCHECK, BST_UNCHECKED, 0 );
            SendDlgItemMessage( m_hWnd, IDC_EVENTS, BM_SETCHECK, BST_UNCHECKED, 0 );
            SetDlgItemText( m_hWnd, IDE_EDITBOX, _T("Enter text you wish spoken here.") );

            // reset output format
            SendDlgItemMessage( m_hWnd, IDC_COMBO_OUTPUT, CB_SETCURSEL, m_DefaultFormatIndex, 0 );
            SendMessage( m_hWnd, WM_COMMAND, MAKEWPARAM(IDC_COMBO_OUTPUT, CBN_SELCHANGE), 0 );

            // Change the volume and the rate to reflect what the UI says
            HandleScroll( ::GetDlgItem( m_hWnd, IDC_VOLUME_SLIDER ) );
            HandleScroll( ::GetDlgItem( m_hWnd, IDC_RATE_SLIDER ) );

            SetFocus( hwndEdit );
            break;

        case IDC_COMBO_OUTPUT:
            if( codeNotify == CBN_SELCHANGE )
            {
                // Get the audio output format and set it's GUID
                iFormat  = SendDlgItemMessage( m_hWnd, IDC_COMBO_OUTPUT, CB_GETCURSEL, 0, 0 );
                SPSTREAMFORMAT eFmt = (SPSTREAMFORMAT)SendDlgItemMessage( m_hWnd, IDC_COMBO_OUTPUT,
                                                        CB_GETITEMDATA, iFormat, 0 );
                CSpStreamFormat Fmt;
                Fmt.AssignFormat(eFmt);
                if ( m_cpOutAudio )
                {
                    hr = m_cpOutAudio->SetFormat( Fmt.FormatId(), Fmt.WaveFormatExPtr() );
                }
                else
                {
                    hr = E_FAIL;
                }

                if( SUCCEEDED( hr ) )
                {
                    hr = m_cpVoice->SetOutput( m_cpOutAudio, FALSE );
                }

                if( FAILED( hr ) )
                {
                    TTSAppStatusMessage( m_hWnd, _T("Format rejected\r\n") );
                }

                EnableSpeakButtons( SUCCEEDED( hr ) );
            }
            break;

        case IDC_SAVETOWAV:
        {
            TCHAR szFileName[256];
            _tcscpy_s(szFileName, _countof(szFileName), _T("\0"));

            bFileOpened = CallSaveFileDialog( szFileName,
                        _T("WAV (*.wav)\0*.wav\0All Files (*.*)\0*.*\0") );

            if (bFileOpened == FALSE) break;

            wcscpy_s( m_szWFileName, _countof(m_szWFileName), CT2W(szFileName) );

            CSpStreamFormat OriginalFmt;
            hr = m_cpVoice->GetOutputStream( &cpOldStream );
            if (hr == S_OK)
            {
                hr = OriginalFmt.AssignFormat(cpOldStream);
            }
            else
            {
                hr = E_FAIL;
            }
            // User SAPI helper function in sphelper.h to create a wav file
            if (SUCCEEDED(hr))
            {
                hr = SPBindToFile( m_szWFileName, SPFM_CREATE_ALWAYS, &cpWavStream, &OriginalFmt.FormatId(), OriginalFmt.WaveFormatExPtr() ); 
            }
            if( SUCCEEDED( hr ) )
            {
                // Set the voice's output to the wav file instead of the speakers
                hr = m_cpVoice->SetOutput(cpWavStream, TRUE);
            }

            if ( SUCCEEDED( hr ) )
            {
                // Do the Speak
                HandleSpeak();
            }

            // Set output back to original stream
            // Wait until the speak is finished if saving to a wav file so that
            // the smart pointer cpWavStream doesn't get released before its
            // finished writing to the wav.
            m_cpVoice->WaitUntilDone( INFINITE );
            cpWavStream.Release();
            
            // Reset output
            m_cpVoice->SetOutput( cpOldStream, FALSE );
            
            TCHAR   szTitle[MAX_PATH];
            TCHAR   szConfString[MAX_PATH];
            if ( SUCCEEDED( hr ) )
            {
                LoadString( m_hInst, IDS_SAVE_NOTIFY, szConfString, MAX_PATH );
                LoadString( m_hInst, IDS_NOTIFY_TITLE, szTitle, MAX_PATH );
                MessageBox( m_hWnd, szConfString, szTitle, MB_OK | MB_ICONINFORMATION );
            }
            else
            {
                LoadString( m_hInst, IDS_SAVE_ERROR, szConfString, MAX_PATH );
                MessageBox( m_hWnd, szConfString, NULL, MB_ICONEXCLAMATION );
            }

            break;
        }
    }
    
    return;
}
BOOL CTTSApp::OnInitDialog( HWND hWnd )
/////////////////////////////////////////////////////////////////
{
    HRESULT                         hr          = S_OK;
        
    // Store this as the "Main Dialog"
    m_hWnd  = hWnd;

    // Add some default text to the main edit control
    SetDlgItemText( hWnd, IDE_EDITBOX, _T("Enter text you wish spoken here.") );

    // Set the event mask in the rich edit control so that it notifies us when text is
    // changed in the control
    SendMessage( GetDlgItem( hWnd, IDE_EDITBOX ), EM_SETEVENTMASK, 0, ENM_CHANGE );

    // Initialize the Output Format combo box
    int i;
    for( i=0; i<NUM_OUTPUTFORMATS; i++ )
    {
        SendDlgItemMessage( hWnd, IDC_COMBO_OUTPUT, CB_ADDSTRING, 0,
                    (LPARAM)g_aszOutputFormat[i] );

        SendDlgItemMessage( hWnd, IDC_COMBO_OUTPUT, CB_SETITEMDATA, i, 
                    (LPARAM)g_aOutputFormat[i] );
    }

    if ( !m_cpVoice )
    {
        hr = E_FAIL;
    }

    // Set the default output format as the current selection.
    if( SUCCEEDED( hr ) )
    {
        CComPtr<ISpStreamFormat> cpStream;
        HRESULT hrOutputStream = m_cpVoice->GetOutputStream(&cpStream);

        if (hrOutputStream == S_OK)
        {
            CSpStreamFormat Fmt;
            hr = Fmt.AssignFormat(cpStream);
            if (SUCCEEDED(hr))
            {
                SPSTREAMFORMAT eFmt = Fmt.ComputeFormatEnum();
                for( i=0; i<NUM_OUTPUTFORMATS; i++ )
                {
                    if( g_aOutputFormat[i] == eFmt )
                    {
                        m_DefaultFormatIndex = i;
                        SendDlgItemMessage( hWnd, IDC_COMBO_OUTPUT, CB_SETCURSEL, m_DefaultFormatIndex, 0 );
                    }
                }
            }
        }
        else
        {
            SendDlgItemMessage( hWnd, IDC_COMBO_OUTPUT, CB_SETCURSEL, 0, 0 );
        }
    }

    // Use the SAPI5 helper function in sphelper.h to initialize the Voice combo box.
    if ( SUCCEEDED( hr ) )
    {
        hr = SpInitTokenComboBox( GetDlgItem( hWnd, IDC_COMBO_VOICES ), SPCAT_VOICES );
    }
    
    if ( SUCCEEDED( hr ) )
    {
        SpCreateDefaultObjectFromCategoryId( SPCAT_AUDIOOUT, &m_cpOutAudio );
    }

    // Set default voice data 
    VoiceChange();

    // Set Range for Skip Edit Box...
    SendDlgItemMessage( hWnd, IDC_SKIP_SPIN, UDM_SETRANGE, TRUE, MAKELONG( 50, -50 ) );

    // Set the notification message for the voice
    if ( SUCCEEDED( hr ) )
    {
        m_cpVoice->SetNotifyWindowMessage( hWnd, WM_TTSAPPCUSTOMEVENT, 0, 0 );
    }

    // We're interested in all TTS events
    if( SUCCEEDED( hr ) )
    {
        hr = m_cpVoice->SetInterest( SPFEI_ALL_TTS_EVENTS, SPFEI_ALL_TTS_EVENTS );
    }

    // Get default rate and volume
    if( SUCCEEDED( hr ) )
    {
        hr = m_cpVoice->GetRate( &m_DefaultRate );
        // initialize sliders and edit boxes with default rate
        if ( SUCCEEDED( hr ) )
        {
            SendDlgItemMessage( hWnd, IDC_RATE_SLIDER, TBM_SETRANGE, TRUE, MAKELONG( SPMIN_RATE, SPMAX_RATE ) );
            SendDlgItemMessage( hWnd, IDC_RATE_SLIDER, TBM_SETPOS, TRUE, m_DefaultRate );
            SendDlgItemMessage( hWnd, IDC_RATE_SLIDER, TBM_SETPAGESIZE, TRUE, 5 );
        }
    }

    if( SUCCEEDED( hr ) )
    {
        hr = m_cpVoice->GetVolume( &m_DefaultVolume );
        // initialize sliders and edit boxes with default volume
        if ( SUCCEEDED( hr ) )
        {
            SendDlgItemMessage( hWnd, IDC_VOLUME_SLIDER, TBM_SETRANGE, TRUE, MAKELONG( SPMIN_VOLUME, SPMAX_VOLUME ) );
            SendDlgItemMessage( hWnd, IDC_VOLUME_SLIDER, TBM_SETPOS, TRUE, m_DefaultVolume );
            SendDlgItemMessage( hWnd, IDC_VOLUME_SLIDER, TBM_SETPAGESIZE, TRUE, 10 );
        }
    }

    // If any SAPI initialization failed, shut down!
    if( FAILED( hr ) )
    {
        MessageBox( NULL, _T("Error initializing speech objects. Shutting down."), _T("Error"), MB_OK );
        SendMessage( hWnd, WM_CLOSE, 0, 0 );
        return(FALSE);
        
    }
    else
    {
        //
        // Create the child windows to which we'll blit our result
        //
        HWND hCharWnd = GetDlgItem(hWnd, IDC_CHARACTER);
        RECT rc;

        GetClientRect(hCharWnd, &rc);
        rc.left = (rc.right - CHARACTER_WIDTH) / 2;
        rc.top = (rc.bottom - CHARACTER_HEIGHT) / 2;
        m_hChildWnd = CreateWindow( CHILD_CLASS, NULL, 
                            WS_CHILDWINDOW | WS_VISIBLE,
                            rc.left, rc.top,
                            rc.left + CHARACTER_WIDTH, rc.top + CHARACTER_HEIGHT,
                            hCharWnd, NULL, m_hInst, NULL );

        if ( !m_hChildWnd )
        {
            MessageBox( hWnd, _T("Error initializing speech objects. Shutting down."), _T("Error"), MB_OK );
            SendMessage( hWnd, WM_CLOSE, 0, 0 );
            return(FALSE);
            
        }
        else
        {
            // Load Mouth Bitmaps and use and ImageList since we'll blit the mouth
            // and eye positions over top of the full image
            g_hListBmp = InitImageList();
        }
    }
    return(TRUE);
    
}
Beispiel #3
0
	void Sound::test() {

		ISpVoice * pVoice = NULL;
		ISpObjectToken*        pVoiceToken=nullptr;
		IEnumSpObjectTokens*   pEnum;
		ULONG                  ulCount = 0;

		if (FAILED(::CoInitialize(NULL)))
		{
			return;
		}
		HRESULT hr = S_OK;

		// Find the best matching installed en-us recognizer.
		CComPtr<ISpObjectToken> cpRecognizerToken;

		if (SUCCEEDED(hr))
		{
			hr = SpFindBestToken(SPCAT_RECOGNIZERS, L"language=409", NULL, &cpRecognizerToken);
		}

		// Create the in-process recognizer and immediately set its state to inactive.
		CComPtr<ISpRecognizer> cpRecognizer;

		if (SUCCEEDED(hr))
		{
			hr = cpRecognizer.CoCreateInstance(CLSID_SpInprocRecognizer);
		}

		if (SUCCEEDED(hr))
		{
			hr = cpRecognizer->SetRecognizer(cpRecognizerToken);
		}

		if (SUCCEEDED(hr))
		{
			hr = cpRecognizer->SetRecoState(SPRST_INACTIVE);
		}

		// Create a new recognition context from the recognizer.
		CComPtr<ISpRecoContext> cpContext;

		if (SUCCEEDED(hr))
		{
			hr = cpRecognizer->CreateRecoContext(&cpContext);
		}

		// Subscribe to the speech recognition event and end stream event.
		if (SUCCEEDED(hr))
		{
			ULONGLONG ullEventInterest = SPFEI(SPEI_RECOGNITION);
			hr = cpContext->SetInterest(ullEventInterest, ullEventInterest);
		}

		// Establish a Win32 event to signal when speech events are available.
		HANDLE hSpeechNotifyEvent = INVALID_HANDLE_VALUE;

		if (SUCCEEDED(hr))
		{
			hr = cpContext->SetNotifyWin32Event();
		}

		if (SUCCEEDED(hr))
		{
			hSpeechNotifyEvent = cpContext->GetNotifyEventHandle();

			if (INVALID_HANDLE_VALUE == hSpeechNotifyEvent)
			{
				// Notification handle unsupported.
				hr = E_NOINTERFACE;
			}
		}

		// Initialize an audio object to use the default audio input of the system and set the recognizer to use it.
		CComPtr<ISpAudio> cpAudioIn;

		if (SUCCEEDED(hr))
		{
			hr = cpAudioIn.CoCreateInstance(CLSID_SpMMAudioIn);
		}

		if (SUCCEEDED(hr))
		{
			hr = cpRecognizer->SetInput(cpAudioIn, TRUE);
		}

		// Populate a WAVEFORMATEX struct with our desired output audio format. information.
		WAVEFORMATEX* pWfexCoMemRetainedAudioFormat = NULL;
		GUID guidRetainedAudioFormat = GUID_NULL;

		if (SUCCEEDED(hr))
		{
			hr = SpConvertStreamFormatEnum(SPSF_16kHz16BitMono, &guidRetainedAudioFormat, &pWfexCoMemRetainedAudioFormat);
		}

		// Instruct the recognizer to retain the audio from its recognition results.
		if (SUCCEEDED(hr))
		{
			hr = cpContext->SetAudioOptions(SPAO_RETAIN_AUDIO, &guidRetainedAudioFormat, pWfexCoMemRetainedAudioFormat);
		}

		if (NULL != pWfexCoMemRetainedAudioFormat)
		{
			CoTaskMemFree(pWfexCoMemRetainedAudioFormat);
		}

		// Create a new grammar and load an SRGS grammar from file.
		CComPtr<ISpRecoGrammar> cpGrammar;

		if (SUCCEEDED(hr))
		{
			hr = cpContext->CreateGrammar(0, &cpGrammar);
		}

		if (SUCCEEDED(hr))
		{
			hr = cpGrammar->LoadCmdFromFile(L"grammar.grxml", SPLO_STATIC);
		}

		// Set all top-level rules in the new grammar to the active state.
		if (SUCCEEDED(hr))
		{
			hr = cpGrammar->SetRuleState(NULL, NULL, SPRS_ACTIVE);
		}

		// Set the recognizer state to active to begin recognition.
		if (SUCCEEDED(hr))
		{
			hr = cpRecognizer->SetRecoState(SPRST_ACTIVE_ALWAYS);
		}

		// Establish a separate Win32 event to signal the event loop exit.
		HANDLE hExitEvent = CreateEventW(NULL, FALSE, FALSE, NULL);

		// Collect the events listened for to pump the speech event loop.
		HANDLE rghEvents[] = { hSpeechNotifyEvent, hExitEvent };

		// Speech recognition event loop.
		BOOL fContinue = TRUE;

		while (fContinue && SUCCEEDED(hr))
		{
			// Wait for either a speech event or an exit event, with a 15 second timeout.
			DWORD dwMessage = WaitForMultipleObjects(sp_countof(rghEvents), rghEvents, FALSE, 15000);

			switch (dwMessage)
			{
				// With the WaitForMultipleObjects call above, WAIT_OBJECT_0 is a speech event from hSpeechNotifyEvent.
			case WAIT_OBJECT_0:
			{
				// Sequentially grab the available speech events from the speech event queue.
				CSpEvent spevent;

				while (S_OK == spevent.GetFrom(cpContext))
				{
					switch (spevent.eEventId)
					{
					case SPEI_RECOGNITION:
					{
						// Retrieve the recognition result and output the text of that result.
						ISpRecoResult* pResult = spevent.RecoResult();

						LPWSTR pszCoMemResultText = NULL;
						hr = pResult->GetText(SP_GETWHOLEPHRASE, SP_GETWHOLEPHRASE, TRUE, &pszCoMemResultText, NULL);

						if (SUCCEEDED(hr))
						{
							wprintf(L"Recognition event received, text=\"%s\"\r\n", pszCoMemResultText);
						}

						// Also retrieve the retained audio we requested.
						CComPtr<ISpStreamFormat> cpRetainedAudio;

						if (SUCCEEDED(hr))
						{
							hr = pResult->GetAudio(0, 0, &cpRetainedAudio);
						}

						// To demonstrate, we'll speak the retained audio back using ISpVoice.
						CComPtr<ISpVoice> cpVoice;

						if (SUCCEEDED(hr))
						{
							hr = cpVoice.CoCreateInstance(CLSID_SpVoice);
						}

						if (SUCCEEDED(hr))
						{
							hr = cpVoice->SpeakStream(cpRetainedAudio, SPF_DEFAULT, 0);
						}

						if (NULL != pszCoMemResultText)
						{
							CoTaskMemFree(pszCoMemResultText);
						}

						break;
					}
					}
				}

				break;
			}
			case WAIT_OBJECT_0 + 1:
			case WAIT_TIMEOUT:
			{
				// Exit event or timeout; discontinue the speech loop.
				fContinue = FALSE;
				//break;
			}
			}
		}

	CoUninitialize();

		CComPtr <ISpVoice>		cpVoice;
		CComPtr <ISpStream>		cpStream;
		CSpStreamFormat			cAudioFmt;

		//Create a SAPI Voice
		hr = cpVoice.CoCreateInstance(CLSID_SpVoice);

		//Set the audio format
		if (SUCCEEDED(hr))
		{
			hr = cAudioFmt.AssignFormat(SPSF_22kHz16BitMono);
		}

		//Call SPBindToFile, a SAPI helper method,  to bind the audio stream to the file
		if (SUCCEEDED(hr))
		{

			hr = SPBindToFile(L"c:\\ttstemp.wav", SPFM_CREATE_ALWAYS,
				&cpStream, &cAudioFmt.FormatId(), cAudioFmt.WaveFormatExPtr());
		}

		//set the output to cpStream so that the output audio data will be stored in cpStream
		if (SUCCEEDED(hr))
		{
			hr = cpVoice->SetOutput(cpStream, TRUE);
		}

		//Speak the text "hello world" synchronously
		if (SUCCEEDED(hr))
		{
			hr = cpVoice->Speak(L"Hello World", SPF_DEFAULT, NULL);
		}

		//close the stream
		if (SUCCEEDED(hr))
		{
			hr = cpStream->Close();
		}

		//Release the stream and voice object
		cpStream.Release();
		cpVoice.Release();

		CComPtr<ISpGrammarBuilder>    cpGrammarBuilder;
		SPSTATEHANDLE                 hStateTravel;
		// Create (if rule does not already exist)
		// top-level Rule, defaulting to Active.
		hr = cpGrammarBuilder->GetRule(L"Travel", 0, SPRAF_TopLevel | SPRAF_Active, TRUE, &hStateTravel);

		// Approach 1: List all possible phrases.
		// This is the most intuitive approach, and it does not sacrifice efficiency
		// because the grammar builder will merge shared sub-phrases when possible.
		// There is only one root state, hStateTravel, and the terminal NULL state,
		// and there are six unique transitions between root state and NULL state.

		/* XML Approximation:
		<rule id="Travel">
		<item> fly to Seattle </item>
		<item> fly to New York </item>
		<item> fly to Washington DC </item>
		<item> drive to Seattle </item>
		<item> drive to New York </item>
		<item> drive to Washington DC </item>
		</rule>
		*/

		// Create set of peer phrases, each containing complete phrase.
		// Note: the word delimiter is set as " ", so that the text we
		// attach to the transition can be multiple words (for example,
		// "fly to Seattle" is implicitly "fly" + "to" + "Seattle"):
		if (SUCCEEDED(hr))
		{
			hr = cpGrammarBuilder->AddWordTransition(hStateTravel, NULL, L"fly to Seattle", L" ", SPWT_LEXICAL, 1, NULL);
		}
		if (SUCCEEDED(hr))
		{
			hr = cpGrammarBuilder->AddWordTransition(hStateTravel, NULL, L"fly to New York", L" ", SPWT_LEXICAL, 1, NULL);
		}
		if (SUCCEEDED(hr))
		{
			hr = cpGrammarBuilder->AddWordTransition(hStateTravel, NULL, L"fly to Washington DC", L" ", SPWT_LEXICAL, 1, NULL);
		}
		if (SUCCEEDED(hr))
		{
			hr = cpGrammarBuilder->AddWordTransition(hStateTravel, NULL, L"drive to Seattle", L" ", SPWT_LEXICAL, 1, NULL);
		}
		if (SUCCEEDED(hr))
		{
			hr = cpGrammarBuilder->AddWordTransition(hStateTravel, NULL, L"drive to New York", L" ", SPWT_LEXICAL, 1, NULL);
		}
		if (SUCCEEDED(hr))
		{
			hr = cpGrammarBuilder->AddWordTransition(hStateTravel, NULL, L"drive to Washington DC", L" ", SPWT_LEXICAL, 1, NULL);
		}
		// Find the best matching installed en-US recognizer.
		//CComPtr<ISpObjectToken> cpRecognizerToken;

		if (SUCCEEDED(hr))
		{
			hr = SpFindBestToken(SPCAT_RECOGNIZERS, L"language=409", NULL, &cpRecognizerToken);
		}

		// Create the in-process recognizer and immediately set its state to inactive.
		//CComPtr<ISpRecognizer> cpRecognizer;

		if (SUCCEEDED(hr))
		{
			hr = cpRecognizer.CoCreateInstance(CLSID_SpInprocRecognizer);
		}

		if (SUCCEEDED(hr))
		{
			hr = cpRecognizer->SetRecognizer(cpRecognizerToken);
		}

		if (SUCCEEDED(hr))
		{
			hr = cpRecognizer->SetRecoState(SPRST_INACTIVE);
		}

		// Create a new recognition context from the recognizer.
		//CComPtr<ISpRecoContext> cpContext;

		if (SUCCEEDED(hr))
		{
			hr = cpRecognizer->CreateRecoContext(&cpContext);
		}

		// Subscribe to the speech recognition event and end stream event.
		if (SUCCEEDED(hr))
		{
			ULONGLONG ullEventInterest = SPFEI(SPEI_RECOGNITION) | SPFEI(SPEI_END_SR_STREAM);
			hr = cpContext->SetInterest(ullEventInterest, ullEventInterest);
		}

		// Establish a Win32 event to signal when speech events are available.
		//HANDLE hSpeechNotifyEvent = INVALID_HANDLE_VALUE;

		if (SUCCEEDED(hr))
		{
			hr = cpContext->SetNotifyWin32Event();
		}

		if (SUCCEEDED(hr))
		{
			hr = cpContext->SetNotifyWin32Event();
		}

		if (SUCCEEDED(hr))
		{
			hSpeechNotifyEvent = cpContext->GetNotifyEventHandle();

			if (INVALID_HANDLE_VALUE == hSpeechNotifyEvent)
			{
				// Notification handle unsupported
				//hr = SPERR_UNITIALIZED;
			}
		}
		// Set up an audio input stream using a .wav file and set the recognizer's input.
		CComPtr<ISpStream> cpInputStream;

		if (SUCCEEDED(hr))
		{
			hr = SPBindToFile(L"Test.wav", SPFM_OPEN_READONLY, &cpInputStream);
		}

		if (SUCCEEDED(hr))
		{
			hr = cpRecognizer->SetInput(cpInputStream, TRUE);
		}

		// Create a new grammar and load an SRGS grammar from file.
		//CComPtr<ISpRecoGrammar> cpGrammar;

		if (SUCCEEDED(hr))
		{
			hr = cpContext->CreateGrammar(0, &cpGrammar);
		}

		if (SUCCEEDED(hr))
		{
			hr = cpGrammar->LoadCmdFromFile(L"grammar.grxml", SPLO_STATIC);
		}

		// Set all top-level rules in the new grammar to the active state.
		if (SUCCEEDED(hr))
		{
			hr = cpGrammar->SetRuleState(NULL, NULL, SPRS_ACTIVE);
		}

		// Finally, set the recognizer state to active to begin recognition.
		if (SUCCEEDED(hr))
		{
			hr = cpRecognizer->SetRecoState(SPRST_ACTIVE_ALWAYS);
		}

		 hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void     **)&pVoice);
		if (SUCCEEDED(hr)) {
			hr = SpEnumTokens(SPCAT_VOICES, L"Gender=Female", NULL, &pEnum);
			if (SUCCEEDED(hr))
			{
				// Get the number of voices.
				hr = pEnum->GetCount(&ulCount);
			}

			// Obtain a list of available voice tokens, set
			// the voice to the token, and call Speak.
			while (SUCCEEDED(hr) && ulCount--)			{
				if (pVoiceToken != nullptr) {
					pVoiceToken->Release();
				}

				if (SUCCEEDED(hr))
				{
					hr = pEnum->Next(1, &pVoiceToken, NULL);
				}

				if (SUCCEEDED(hr))
				{
					hr = pVoice->SetVoice(pVoiceToken);
				}

				if (SUCCEEDED(hr))
				{
					wchar_t* start = L"<?xml version=\"1.0\" encoding=\"ISO - 8859 - 1\"?><speak version = \"1.0\" xmlns = \"http://www.w3.org/2001/10/synthesis\"	xml:lang = \"en-US\">";
					wchar_t* end = L"</speak>";
					const wchar_t *xml = L"<voice required = \"Gender=Male\"> hi! <prosody pitch=\"fast\"> This is low pitch. </prosody><prosody volume=\"x - loud\"> This is extra loud volume. </prosody>";
					wstring s = start;
					s += xml;
					s += end;
					
					hr = pVoice->Speak(xml, SPF_IS_XML| SPF_ASYNC, 0);
					//hr = pVoice->Speak(L"How are you?", SPF_DEFAULT, NULL);
				}

			}
			/*
			if (SUCCEEDED(hr)) {
				hr = pEnum->Next(1, &pVoiceToken, NULL);
				if (SUCCEEDED(hr)) {
					hr = pVoice->SetVoice(pVoiceToken);
					// Set the output to the default audio device.
					if (SUCCEEDED(hr)) {
						hr = pVoice->SetOutput(NULL, TRUE);
						if (SUCCEEDED(hr)) {
							hr = pVoice->Speak(L"Hello, world!", SPF_DEFAULT, 0);
						}
					}
				}
			}
			*/
			pVoice->Release();
		}
		::CoUninitialize();
	}
Beispiel #4
0
//Speech Initialization is done here
HRESULT CASRwrapper::InitSpeech(std::wstring sPathToFile, IStream * pMemStream)
{
	HRESULT hr = S_OK;

	hr = cpRecoEngine.CoCreateInstance(CLSID_SpInprocRecognizer);

	if (SUCCEEDED(hr))
	{
		hr = cpRecoEngine->CreateRecoContext(&m_cpRecoCtxt);
	}

	if (SUCCEEDED(hr))
	{
		WPARAM wparam = NULL;
		LPARAM lparam = NULL;
		hr = m_cpRecoCtxt->SetNotifyWin32Event();
		//hr = m_cpRecoCtxt->SetNotifyCallbackFunction(SpRecCallback,wparam,lparam);
		//	hr = m_cpRecoCtxt->SetNotifyWindowMessage(m_hWnd, WM_RECOEVENT, 0, 0);
	}

	if (SUCCEEDED(hr))
	{
		// This specifies which of the recognition events are going 
		//to trigger notifications. Here, all we are interested in 
		//is the beginning and ends of sounds, as well as
		// when the engine has recognized something
		//using ISpRecoContext
		const ULONGLONG ullInterest = SPFEI(SPEI_RECOGNITION);
		hr = m_cpRecoCtxt->SetInterest(ullInterest, ullInterest);
	}

	if (SUCCEEDED(hr))
	{
		// Specifies that the grammar we want is a dictation grammar.
		// Initializes the grammar (m_cpDictationGrammar)
		// using ISpRecoContext
		hr = m_cpRecoCtxt->CreateGrammar(GID_DICTATION, &m_cpDictationGrammar);
	}

	if (SUCCEEDED(hr))
	{
		//Load the dictation tool for the grammar specified
		//using ISpRecoGrammar
		hr = m_cpDictationGrammar->LoadDictation(NULL, SPLO_STATIC);
	}

	if (!sPathToFile.empty() || pMemStream != NULL)
	{
		CComPtr<ISpStream> cpInputStream;
		if (SUCCEEDED(hr))
		{
			// Create basic SAPI stream object
			// NOTE: The helper SpBindToFile can be used to perform the following operations
			hr = cpInputStream.CoCreateInstance(CLSID_SpStream);
		}

		CSpStreamFormat sInputFormat;
		// generate WaveFormatEx structure, assuming the wav format is 44kHz, 16-bit, Mono
		if (SUCCEEDED(hr))
		{
			hr = sInputFormat.AssignFormat(SPSF_44kHz16BitMono);
		}

		if (pMemStream != NULL)
		{
			if (SUCCEEDED(hr))
			{
				hr = cpInputStream->SetBaseStream(pMemStream, SPDFID_WaveFormatEx, sInputFormat.WaveFormatExPtr());
			}
		}
		else
		{
			if (SUCCEEDED(hr))
			{
				//   for read-only access, since it will only be access by the SR engine
				hr = cpInputStream->BindToFile(sPathToFile.c_str(),
					SPFM_OPEN_READONLY,
					&(sInputFormat.FormatId()),
					sInputFormat.WaveFormatExPtr(),
					SPFEI_ALL_EVENTS);
			}
		}

		if (SUCCEEDED(hr))
		{
			// connect wav input to recognizer
			// SAPI will negotiate mismatched engine/input audio formats using system audio codecs, so second parameter is not important - use default of TRUE
			hr = cpRecoEngine->SetInput(cpInputStream, TRUE);
		}

	}
	else //connect to mic
	{
		// create default audio object
		CComPtr<ISpAudio> cpAudio;
		if (SUCCEEDED(hr))
		{
			hr = SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOIN, &cpAudio);
		}

		// set the input for the engine
		if (SUCCEEDED(hr))
		{
			hr = cpRecoEngine->SetInput(cpAudio, TRUE);
		}

		if (SUCCEEDED(hr))
		{
			hr = cpRecoEngine->SetRecoState(SPRST_ACTIVE);
		}
	}


	if (FAILED(hr))
	{
		//Release the grammar using ISpRecoGrammar
		m_cpDictationGrammar.Release();
	}

	return hr;
}
Beispiel #5
0
void sapi::run (const char * text, unsigned samplerate, const char * path)
{
	HRESULT hr = E_FAIL;

	ISpStreamFormat_memblock * pSpStreamFormat_memblock = new ISpStreamFormat_memblock;
	mmh::comptr_t<ISpStreamFormat> pSpStreamFormat = pSpStreamFormat_memblock, pCurSpStreamFormat;

#if 0
	CSpStreamFormat	cAudioFmt;
	mmh::comptr_t<ISpStream> cpStream;

	hr = cAudioFmt.AssignFormat(SPSF_16kHz16BitMono);
	_check_hresult(hr);

	hr = SPBindToFile( pfc::stringcvt::string_wide_from_utf8(path),  SPFM_CREATE_ALWAYS, cpStream.get_pp(), &cAudioFmt.FormatId(), cAudioFmt.WaveFormatExPtr());
	_check_hresult(hr);
#endif

	hr = m_SpVoice->GetOutputStream(pCurSpStreamFormat.get_pp());
	_check_hresult(hr);

	GUID fmt; WAVEFORMATEX * wfe = NULL;
	hr = pCurSpStreamFormat->GetFormat(&fmt, &wfe);
	_check_hresult(hr);
	if (wfe == NULL)
		_check_hresult(E_FAIL);
	if (wfe->nChannels > 1)
	{
		wfe->nBlockAlign /= wfe->nChannels;
		wfe->nAvgBytesPerSec/= wfe->nChannels;
		wfe->nChannels = 1;
	}
	hr = pSpStreamFormat_memblock->AssignFormat(wfe);
	if (wfe) CoTaskMemFree(wfe);
	_check_hresult(hr);

#if 0
	hr = m_SpVoice->SetOutput( cpStream, FALSE );
#else
	hr = m_SpVoice->SetOutput( pSpStreamFormat, FALSE );
#endif
	_check_hresult(hr);

	hr = m_SpVoice->Speak( pfc::stringcvt::string_wide_from_utf8(text),  SPF_DEFAULT, NULL );
	_check_hresult(hr);

#if 0
	hr = cpStream->Close();
	return;
#endif

	const WAVEFORMATEX * pwfex = pSpStreamFormat_memblock->WaveFormatExPtr();
	if (pwfex == NULL)
		_check_hresult(E_FAIL);

	{
		static_api_ptr_t<audio_postprocessor> processor;
		dsp::ptr resampler;
		dsp_chunk_list_impl resampler_chunks;
		audio_chunk_impl chunk;
		mem_block_container_impl_t<pfc::alloc_fast_aggressive> chunk2;
		pfc::array_t<t_uint8, pfc::alloc_fast_aggressive> finalOutStream;

		if (!resampler_entry::g_create(resampler, chunk.get_sample_rate(), samplerate, 1.0))
			throw pfc::exception( pfc::string8() << "Could not create resampler (" << chunk.get_sample_rate() << " Hz -> " << samplerate << " Hz)");

		chunk.set_data_fixedpoint(pSpStreamFormat_memblock->get_ptr(), pSpStreamFormat_memblock->get_size(), pwfex->nSamplesPerSec, pwfex->nChannels, pwfex->wBitsPerSample, 1);
		resampler_chunks.add_chunk(&chunk);
		resampler->run(&resampler_chunks, metadb_handle_ptr(), dsp::FLUSH);

		t_riff_header riff;
		riff.id0 = 'R'|'I'<<8|'F'<<16|'F'<<24;
		riff.id1 = 'f'|'m'<<8|'t'<<16|' '<<24;
		riff.type0 = 'W'|'A'<<8|'V'<<16|'E'<<24;
		riff.id2 = 'd'|'a'<<8|'t'<<16|'a'<<24;
		riff.headersize = sizeof(riff.header);

		riff.header.wFormatTag=WAVE_FORMAT_PCM;

		riff.header.nSamplesPerSec = samplerate;//pwfex->nSamplesPerSec;
		riff.header.nChannels = pwfex->nChannels;
		riff.header.wBitsPerSample = pwfex->wBitsPerSample;
		riff.header.nBlockAlign=(riff.header.nChannels*riff.header.wBitsPerSample )/8;
		riff.header.nAvgBytesPerSec =(riff.header.nBlockAlign*riff.header.nSamplesPerSec );

		riff.datasize = (unsigned)chunk.get_sample_count()*riff.header.nBlockAlign;
		riff.filesize = riff.datasize + sizeof(riff);

		abort_callback_impl p_abort;
		file::ptr p_file;
		filesystem::g_open_write_new(p_file, path, p_abort);
		t_size i, count = resampler_chunks.get_count();
		for (i=0; i<count; i++)
		{
			audio_chunk * pChunk = resampler_chunks.get_item(i);
			if (pChunk)
			{
				processor->run(*pChunk, chunk2, 16, 16, false, 1.0);
				finalOutStream.append_fromptr((t_uint8*)chunk2.get_ptr(), chunk2.get_size());
			}
		}
		resampler_chunks.remove_all();
		riff.datasize = (unsigned)finalOutStream.get_size();
		riff.filesize = riff.datasize + sizeof(riff);
		p_file->write(&riff, sizeof(riff), p_abort);
		p_file->write(finalOutStream.get_ptr(), finalOutStream.get_size(), p_abort);
	}

}
//デバッグ用 認識結果をWaveファイルとして保存する
xreturn::r<bool> Recognition_SAPI::DebugSaveWavFile(const std::string& directory,ISpStreamFormat* streamFormat) const
{
	HRESULT hr;
	_USE_WINDOWS_ENCODING;

	const SPSTREAMFORMAT spFormat = SPSF_22kHz8BitMono;
	CSpStreamFormat Fmt( spFormat, &hr);
	if(FAILED(hr))	 return xreturn::windowsError(hr);

	{
		CSpStreamFormat OriginalFmt;
		{
			OriginalFmt.AssignFormat(streamFormat);

			// basic SAPI-stream for file-based storage
			CComPtr<ISpStream> cpStream;
			{
				ULONG cbWritten = 0;

				// create file on hard-disk for storing recognized audio, and specify audio format as the retained audio format
				std::string fff = directory + "\\" + num2str(time(NULL))+".wav";
				hr = SPBindToFile(_A2W(fff.c_str()) , SPFM_CREATE_ALWAYS, &cpStream, &OriginalFmt.FormatId(), OriginalFmt.WaveFormatExPtr(), SPFEI_ALL_EVENTS);
				if(FAILED(hr))	 return xreturn::windowsError(hr);

				// Continuously transfer data between the two streams until no more data is found (i.e. end of stream)
				// Note only transfer 1000 bytes at a time to creating large chunks of data at one time
				while (TRUE)
				{
					// for logging purposes, the app can retrieve the recognized audio stream length in bytes
					STATSTG stats;
					hr = streamFormat->Stat(&stats, NULL);
					if(FAILED(hr))	 return xreturn::windowsError(hr);

					// create a 1000-byte buffer for transferring
					BYTE bBuffer[1000];
					ULONG cbRead;

					// request 1000 bytes of data from the input stream
					hr = streamFormat->Read(bBuffer, 1000, &cbRead);
					// if data was returned??
					if (SUCCEEDED(hr) && cbRead > 0)
					{
						// then transfer/write the audio to the file-based stream
						hr = cpStream->Write(bBuffer, cbRead, &cbWritten);
						if(FAILED(hr))	 return xreturn::windowsError(hr);
					}

					// since there is no more data being added to the input stream, if the read request returned less than expected, the end of stream was reached, so break data transfer loop
					if (cbRead < 1000)
					{
						break;
					}
				}
			}
			// explicitly close the file-based stream to flush file data and allow app to immediately use the file
			hr = cpStream->Close();
			if(FAILED(hr))	 return xreturn::windowsError(hr);
		}
	}
	return true;
}