SAPI 5.4 audio output parameters - windows

I have the following, very simple code (delphi/Object Pascal) which uses Microsoft SAPI 5.4 to do some Text To Speech
// init
var engine : tspvoice;
var voice : ispeechobjecttoken;
engine:=tspvoice.Create(nil);
// pick the first voice and assign it to the engine
voice := ISpeechObjectToken(0);
engine.Voice := Voice;
// speak
engine.Speak(text, SVSFlagsAsync);
However I don't see any control on the output format (Bits per sample / Frequency). I have a sample app (TTSapp) coming from an old SAPI SDKdownload, which allows to control such parameters. Unfortunately I can't locate its source code anywhere, and I can't find any download to an up-to-date sapi SDK too.
Any hints?

I typically don't use delphi, but I do have an example in C++ if it helps. In this case you can see I'm messing with the CSpStreamFormat and then calling SPBindToFile to set it. sapi.h has all the SPSTREAMFORMAT types you can use.
HRESULT hr = S_OK;
CComPtr<ISpObjectToken> cpVoiceToken;
CComPtr<ISpVoice> cpVoice;
CSpStreamFormat cAudioFmt;
CComPtr<ISpStream> cpStream;
::CoInitialize(NULL);
hr = cAudioFmt.AssignFormat(SPSF_22kHz16BitMono);
hr = SPBindToFile(L"E:\\fileName.wav", SPFM_CREATE_ALWAYS, &cpStream, &cAudioFmt.FormatId(), cAudioFmt.WaveFormatExPtr());
hr = cpVoice.CoCreateInstance(CLSID_SpVoice);
cpVoice->SetOutput(cpStream, TRUE);
hr = cpVoice->Speak(L"My spoken text goes here", SPF_DEFAULT, NULL);
cpStream->Close();
cpStream.Release();
cpVoice.Release();
::CoUninitialize();

Related

Getting the icon for an audio device

In the Sound Control Panel in Windows, and in the Volume Mixer, all audio devices have a specific icon set.
Is it possible to get which icon is set for a device through the Windows API?
Sound Control Panel
Volume Mixer
As suggested by Simon Mourier in the comments above, there is a PKEY that can access that data.
You can follow along with most of this sample and just replace PKEY_Device_FriendlyNamewith PKEY_DeviceClass_IconPath.
In short, it's something like this:
// [...]
IMMDevice *pDevice; // the device you want to look up
HRESULT hr = S_OK;
IPropertyStore *pProps = NULL;
PROPVARIANT varName;
hr = pDevice->OpenPropertyStore(STGM_READ, &pProps);
EXIT_ON_ERROR(hr);
PropVariantInit(&varName);
hr = pProps->GetValue(PKEY_DeviceClass_IconPath, &varName);
EXIT_ON_ERROR(hr);
// [...]

What's the etymology behind type name ID3D10Blob for direct x shaders?

What's the etymology behind type name ID3D10Blob for direct x shaders?
I'm reading http://www.directxtutorial.com/Lesson.aspx?lessonid=11-4-5 and trying to reason about the naming conventions in windows.
I see ID3D10Blob as (I)(D3D10)(Blob).
I = interface?
D3D10 = direct3d 10?
Blob = ?
I've seen "Binary large object" online for Blob, but I'm not sure if that has the same meaning in this context.
What does the blob term mean?
TL;DR: It's just a simple ref-counted container for a variable-length blob of binary data used by the D3DCompiler COM interfaces.
The HLSL compiler produces a 'shader blob' which is just an opaque binary object. It has a size and data. It could be really anything, but in the world of "COM" objects, it was implemented for Windows Vista as ID3D10Blob with the introduction of Direct3D 10.
Historically, Direct3D 9 and earlier had a 'fixed-function' rendering pipeline which means you could use it without HLSL shaders. For Direct3D 10, the 'fixed-function' was removed, so HLSL was required to use it at all. Therefore, a version of the Direct3D HLSL Compiler was added to the OS.
The ID3DBlob interface is what's used for Direct3D 11 or Direct3D 12, but if you look at it closely, it's the same thing.
typedef ID3D10Blob ID3DBlob;
The Direct3D API itself actually doesn't use this specific 'blob' interface. In a C++ STL world, you could use std::vector<uint8_t> as a shader blob:
inline std::vector<uint8_t> ReadData(_In_z_ const wchar_t* name)
{
std::ifstream inFile(name, std::ios::in | std::ios::binary | std::ios::ate);
if (!inFile)
throw std::exception("ReadData");
std::streampos len = inFile.tellg();
if (!inFile)
throw std::exception("ReadData");
std::vector<uint8_t> blob;
blob.resize(size_t(len));
inFile.seekg(0, std::ios::beg);
if (!inFile)
throw std::exception("ReadData");
inFile.read(reinterpret_cast<char*>(blob.data()), len);
if (!inFile)
throw std::exception("ReadData");
inFile.close();
return blob;
}
…
auto vertexShaderBlob = ReadData(L"VertexShader.cso");
ThrowIfFailed(
device->CreateVertexShader(vertexShaderBlob.data(), vertexShaderBlob.size(),
nullptr, m_spVertexShader.ReleaseAndGetAddressOf()));
auto pixelShaderBlob = ReadData(L"PixelShader.cso");
ThrowIfFailed(
device->CreatePixelShader(pixelShaderBlob.data(), pixelShaderBlob.size(),
nullptr, m_spPixelShader.ReleaseAndGetAddressOf()));
See Microsoft Docs and this blog post.

Is it possible to only load a dictation topic while using Shared Recogniser and not with Inproc reconizer in sapi?

When I am using a shared recognizer to a previously registered medical dictation topic , dictation grammar is loaded without error but when I change my recognizer to Inproc recognizer it is not able to load a dictation topic. Is there a way to load dictation topic while still using in proc recognizer?
My code for Loading grammar looks like
CComPtr<ISpObjectToken> cpObjectToken;
CComPtr<ISpAudio> cpAudio;
CComPtr<ISpRecognizer> cpEngine;
CComPtr<ISpRecoContext> cpRecoCtx;
CComPtr<ISpRecoGrammar> cpGram;
hr = cpEngine.CoCreateInstance(CLSID_SpInprocRecognizer);
hr = SpGetDefaultTokenFromCategoryId(SPCAT_AUDIOIN, &cpObjectToken);
hr = cpEngine->SetInput(cpObjectToken, TRUE);
hr = SpCreateDefaultObjectFromCategoryId(SPCAT_AUDIOIN, &cpAudio);
hr = cpEngine->SetInput(cpAudio, TRUE);
hr = cpEngine->CreateRecoContext(&cpRecoCtx);
hr = cpEngine->SetRecognizer(NULL);
hr = cpRecoCtx->CreateGrammar(1, &cpGram);
hr = cpGram->LoadDictation(L"Medical", SPLO_STATIC);
Inproc recognizers don't have a default SR engine, so calling
hr = cpEngine->SetRecognizer(NULL);
won't actually load an engine. I wrote a simple function to load the default recognizer; the code's longish, so I won't put it inline, but you can find it here.
Also, in your other question, you had the topic named "Medical", and here you have it named "Medicine"; they do need to be the same.

Barcode Generation in windows 8

I am trying to encode a particular string into a QR Code. I have been used Zxing lib for this.I am not able to generate the qr code using this piece of code. I have tried using Bitmap but windows 8 doesn't allow System.drawing assembly...
public ZXing.Rendering.PixelData get()
{
string url="abcdefghijkl";
BarcodeWriter _writer = new BarcodeWriter();
_writer.Format = BarcodeFormat.QR_CODE;
_writer.Options.Height = 400;
_writer.Options.Width = 400;
_writer.Options.Margin = 1;
var barcodeImage = _writer.Write("tel:" + url); //tel: prefix for phone numbers
return barcodeImage;
}
You have to use the correct pre-built assembly for your target platform.
There is a binary for WinRT available which doesn't use the Bitmap class.
https://zxingnet.codeplex.com/releases
If you use the version for Windows Metadata (WinMD) you have to convert the
raw bytes of the PixelData to an image. It depends on the application you write
and the things you want to do.
There are sample client projects for nearly every platform available
which show you how to use the library.

How to get correct hDevMode values from CPrintDialogEx (PrintDlgEx)?

I'm displaying a CPrintDialogEx dialog to choose a printer and modify the settings. I set the hDevNames member so that a default printer will be selected, but I leave hDevMode set to NULL. On successful return I pull some values such as paper size out of the returned DEVMODE structure from hDevMode.
I'm having a problem because hDevMode appears to be initialized with the values from the default printer that I passed in, not the printer that was finally selected. How do I get the parameters from the actual selected printer?
As requested here's the relevant part of the code. I've deleted some of it in the interest of space. TOwnedHandle is a smart pointer I wrote for holding a memory handle and locking it automatically.
CPrintDialogEx dlg(PD_ALLPAGES | PD_NOCURRENTPAGE | PD_NOPAGENUMS | PD_NOSELECTION, this);
ASSERT(dlg.m_pdex.hDevMode == NULL);
ASSERT(dlg.m_pdex.hDevNames == NULL);
dlg.m_pdex.hDevNames = GlobalAlloc(GHND, sizeof(DEVNAMES) + iSizeName);
DEVNAMES * pDevNames = (DEVNAMES *) GlobalLock(dlg.m_pdex.hDevNames);
// ...
GlobalUnlock(dlg.m_pdex.hDevNames);
if ((dlg.DoModal() == S_OK) && (dlg.m_pdex.dwResultAction == PD_RESULT_PRINT))
{
TOwnedHandle<DEVMODE> pDevMode = dlg.m_pdex.hDevMode;
TRACE("Printer config = %dx%d %d\n", (int)pDevMode->dmPaperWidth, (int)pDevMode->dmPaperLength, (int)pDevMode->dmOrientation);
// ...
}
Edit: I've determined that I don't get the problem if I don't set the hDevNames parameter. I wonder if I've discovered a Windows bug? This is in XP, I don't have a more recent version of Windows handy to test with.
I've distilled the code into a test that doesn't use MFC, this is strictly a Windows API problem. This is the whole thing, nothing left out except the definition of pDefaultPrinter - but of course it doesn't do anything useful anymore.
PRINTDLGEX ex = {sizeof(PRINTDLGEX)};
ex.hwndOwner = m_hWnd;
ex.Flags = PD_ALLPAGES | PD_NOCURRENTPAGE | PD_NOPAGENUMS | PD_NOSELECTION;
ex.nStartPage = START_PAGE_GENERAL;
#if 1
int iSizeName = (strlen(pDefaultPrinter) + 1) * sizeof(char);
ex.hDevNames = GlobalAlloc(GHND, sizeof(DEVNAMES) + iSizeName);
DEVNAMES * pDevNames = (DEVNAMES *) GlobalLock(ex.hDevNames);
ASSERT(pDevNames != NULL);
pDevNames->wDeviceOffset = sizeof(DEVNAMES);
strcpy((char *)pDevNames + pDevNames->wDeviceOffset, pDefaultPrinter);
GlobalUnlock(ex.hDevNames);
#endif
HRESULT hr = PrintDlgEx(&ex);
if ((hr == S_OK) && (ex.dwResultAction == PD_RESULT_PRINT))
{
DEVMODE * pdm = (DEVMODE *) GlobalLock(ex.hDevMode);
ASSERT(pdm != NULL);
TRACE("Printer config = %dx%d %d\n", (int)pdm->dmPaperWidth, (int)pdm->dmPaperLength, (int)pdm->dmOrientation);
GlobalUnlock(ex.hDevMode);
DEVNAMES * pdn = (DEVNAMES *) GlobalLock(ex.hDevNames);
ASSERT(pdn != NULL);
TRACE(_T("Printer device = %s\n"), (char *)pdn + pdn->wDeviceOffset);
GlobalUnlock(ex.hDevNames);
}
If I can't get a fix, I'd love to hear of a work-around.
After much head scratching I think I've figured it out.
When the dialog comes up initially, the hDevMode member gets filled with the defaults for the printer that is initially selected. If you select a different printer before closing the dialog, that DEVMODE structure is presented to the new printer driver; if the paper size doesn't make sense to the driver it may change it, and the drivers are not consistent.
The reason this tripped me up is that I was switching between three printers: two label
printers with very different characteristics, and a laser printer with US Letter paper.
The laser printer always responds with the proper dimensions but may indicate a wrong paper size code.
The first label printer will override the size provided by the laser printer but not the other label printer.
The second label printer will accept the size provided by the first label printer, because it's capable of using that size even though it's not loaded and not configured. It modifies the size provided by the laser printer by returning the maximum width and the Letter size length of 11 inches.
I determined two ways to work around the problem. The first is to implement IPrintDialogCallback and respond to SelectionChange calls by reloading the default DEVMODE for the newly selected printer. EDIT: I tried this and it does not work. CPrintDialogEx already implements an IPrintDialogCallback interface, making this easy. It appears that PrintDlgEx has its own internal handle that it uses to track the current DEVMODE structure and only uses the one in the PRINTDLGEX structure for input/output. There's no way to affect the DEVMODE while the dialog is up, and by the time it returns it's too late.
The second solution is to ignore the returned results entirely and work from the default paper configuration for the printer. Any changes made from the printer defaults within the dialog are lost completely, but for my application this is acceptable.
bool MyDialog::GetPaperSize(const TCHAR * pPrinterName, double & dPaperWidth, double & dPaperLength)
{
// you need to open the printer before you can get its properties
HANDLE hPrinter;
if (OpenPrinter((TCHAR *)pPrinterName, &hPrinter, NULL))
{
// determine how much space is needed for the DEVMODE structure by the printer driver
int iDevModeSize = DocumentProperties(m_hWnd, hPrinter, (TCHAR *)pPrinterName, NULL, NULL, 0);
ASSERT(iDevModeSize >= sizeof(DEVMODE);
// allocate a DEVMODE structure and initialize it to a clean state
std::vector<char> buffer(iDevModeSize, 0);
DEVMODE * pdm = (DEVMODE *) &buffer[0];
pdm->dmSpecVersion = DM_SPECVERSION;
DocumentProperties(m_hWnd, hPrinter, (TCHAR *)pPrinterName, pdm, NULL, DM_OUT_BUFFER);
ClosePrinter(hPrinter);
// convert paper size from tenths of a mm to inches
dPaperWidth = pdm->dmPaperWidth / 254.;
dPaperLength = pdm->dmPaperLength / 254.;
return true;
}
return false;
}

Resources