Obtain an ISpVoice

//
// 2003 Glenn Slayden
// Obtain an ISpVoice corresponding to a given multimedia output device id. Uses SAPI subsystem. Works on 
// Windows XP. Microsoft documentation tries to force your to use the higher-level Agent API for
// text-to-speech (only Windows CE docs remain on MSDN for SAPI), but you don't need it.  For header 
// files, install SAPI  5.1 SDK, or... required files are included with Visual Studio .NET
//
// typical caller usage:
// 
//		HRESULT hr = GetDeviceVoice(0,&pVoice);
//		if (SUCCEEDED(hr))
//		{
//			pVoice->SetRate(-2);
//			
//			BSTR bs = SysAllocString(L"Who ever heard of a computer which speaks?");
//			hr = pVoice->Speak(bs, 0, NULL);
//			SysFreeString(bs);
//			pVoice->Release();
//		}
//

#define _UNICODE
#include "windows.h"
#include "sapi.h"
#include "sphelper.h"

HRESULT GetDeviceVoice(UINT dev,ISpVoice **ppout)
{
	if (!ppout)
		return E_POINTER;
	*ppout = NULL;

	ISpVoice *pVoice = NULL;
	HRESULT hr = CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&pVoice);

	if (FAILED(hr))
		return hr;

	ISpObjectTokenCategory *cpSpCategory;

	if (SUCCEEDED(hr = SpGetCategoryFromId(SPCAT_AUDIOOUT, &cpSpCategory)))
	{
		IEnumSpObjectTokens *cpSpEnumTokens;

		if (SUCCEEDED(hr = cpSpCategory->EnumTokens(NULL, NULL, &cpSpEnumTokens)))
		{
			ISpObjectToken *pSpTok;

			if (SUCCEEDED(hr = cpSpEnumTokens->Item(dev,&pSpTok)))
			{
				pVoice->SetOutput(pSpTok,FALSE);
				pSpTok->Release();
			}
			cpSpEnumTokens->Release();
		}
		cpSpCategory->Release();
	}

	if (hr==NOERROR)
		*ppout = pVoice;

	return hr;
}