vampi 5 months ago
parent 621fd5b58f
commit 9c444e7aab

@ -1,6 +1,84 @@
SAPI command line interface
A simple tool to generate a `.wav` file from text.
A simple tool to generate audio from text.
Using [getoptW](
Development process
Microsoft can suck a big fat poopy pee pee.
- Install [Visual Studio Build Tools]( ([direct download](, and click on "Desktop Development with C++", on the right make sure to check "C++ ATL for latest ...", and you can uncheck "C++ Cmake tools ...", "Testing tools ..." and "C++ AddressSanitizer", to save on space.
![Build Tools](buildtools.png)
After installing, run "Developer Command Prompt for VS 2022" from the start menu, or just hit "Launch" in Visual Studio Installer. `cd` to the folder where you've cloned this repo, cd to `sapicli`, and type:
`msbuild sapicli.vcxproj -p:Configuration=Release`
* [SAPI XML Tutorial](
* [How to list all SAPI voices](
* [SpEnumTokens examples](
* [List attributes for voices](
* [American English Phoneme Representation](
* [RIFFPad](
EVNT chunk
`.wav` files generated by using [`SPBindToFile()`]( contain an EVNT chunk, which is a list of serialized events, their structure being that of [`SPSERIALIZEDEVENT`]( plus any string referenced inside the event itself.
The first byte is the event type, and most events are 24 bytes long. Strings that follow events are in wide char format. String lengths are padded upwards to multiples of 4. So if the string is 126 bytes, it is stored as 128 bytes, with the last two bytes beign zeroes.
![EVNT Chunk in RIFFPad](riffpad.png)
Excerpt from `sphelper.h`:
* SpSerializedEventSize *
* Description:
* Returns the size, in bytes, used by a serialized event. The caller can
* pass a pointer to either a SPSERIAILZEDEVENT or SPSERIALIZEDEVENT64 structure.
* Returns:
* Number of bytes used by serizlied event
template <class T>
inline ULONG SpSerializedEventSize(const T * pSerEvent)
ULONG ulSize = sizeof(T);
if( ( pSerEvent->elParamType == SPET_LPARAM_IS_POINTER ) && pSerEvent->SerializedlParam )
ulSize += ULONG(pSerEvent->SerializedwParam);
else if ((pSerEvent->elParamType == SPET_LPARAM_IS_STRING || pSerEvent->elParamType == SPET_LPARAM_IS_TOKEN) &&
pSerEvent->SerializedlParam != NULL)
ulSize += ((ULONG)wcslen((WCHAR*)(pSerEvent + 1)) + 1) * sizeof( WCHAR );
// Round up to nearest DWORD
ulSize += 3;
ulSize -= ulSize % 4;
return ulSize;