From 2ced066de2b5af12e3d1d99463aa83b19474b42d Mon Sep 17 00:00:00 2001 From: Fisto Date: Tue, 15 Sep 2026 13:38:36 +0200 Subject: [PATCH] host: volume option; docs: voice adjustments beyond speed (volume, Flextalk profile) --- README.md | 3 ++- docs/NOTES.md | 6 ++++++ docs/TTS-DESIGN.md | 30 ++++++++++++++++++++++++++++-- host/sapi4host.c | 15 ++++++++++++--- 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index aa0b3bd..183d362 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,8 @@ Manual steps, if needed: setup/run.sh -v Sam -t "Hello." -o sam.wav setup/run.sh -v "Keith Bell" -t "Hello." -o keith.wav -p 100 -s 150 -Options: `-v` voice, `-t` text, `-o` output WAV, `-p` pitch, `-s` speed. +Options: `-v` voice, `-t` text, `-o` output WAV, `-p` pitch, `-s` speed, `-V` volume +(volume is engine-specific; not used by the Veskslop wrapper). ## Veskslop integration diff --git a/docs/NOTES.md b/docs/NOTES.md index 618d3c8..173bb61 100644 --- a/docs/NOTES.md +++ b/docs/NOTES.md @@ -82,6 +82,12 @@ final file `WatsonUNInstall.dll` is incomplete). Verified with the corrected DWORD registry: Kerry 0 st = 209 Hz, -6 st = 148 Hz, +6 st = 250 Hz (clamped at max); Keith 0 st = 119 Hz, +6 st = 170 Hz. - The wrapper maps semitones as `natural * 2^(st/12)` and clamps to the range. +- volume: available through `ITTSAttributes` (host flag `-V`). Flextalk is linear + (0-65535, default 65535; half is about -6 dB). MSTTS is non-linear (0 = silence, + 4096 about -24 dB, 65535+ full scale); treat it as on/off. +- Flextalk per-speaker profile values are applied at voice load; `Expression` was + verified audible (0/59/100 produce different waveforms at equal duration). Changing + them requires a registry edit and re-selecting the voice. ## Decompiled corpora (Ghidra) diff --git a/docs/TTS-DESIGN.md b/docs/TTS-DESIGN.md index 3092a39..45f6ed9 100644 --- a/docs/TTS-DESIGN.md +++ b/docs/TTS-DESIGN.md @@ -260,10 +260,36 @@ record what the engine needs to interpret, add, or hide._ ### Text handling - SAPI4 is ANSI/CP1252. The wrapper converts UTF-8 to CP1252 and maps emoji and - unmappable characters to spaces. Engine tag parsing is disabled, so input stays plain - text (no SSML), matching the contract. + unmappable characters to spaces. The host passes `TTSDATAFLAG_TAGGED`, so SAPI4 control + tags in the text are enabled at the engine level; tagged input is not part of this + contract and is untested. Plain text (no SSML) is the contract. - Lines up to 1000 characters are well within engine limits. +### Other voice adjustments (beyond speed) + +All voices implement SAPI4 `ITTSAttributes`, so **pitch, speed and volume** exist at the +API level. The Veskslop contract exposes only speed and pitch; volume is available in the +host as `sapi4host -V ` and is not sent by Veskslop. + +- **Volume** (measured 2026-09-15): + - Flextalk: linear, range 0-65535, default 65535; half (32767) is about -6 dB, 0 is + silence. + - MSTTS: default is 0xFFFFFFFF; 0 is silence, but the response is not linear (for + example 4096 gives about -24 dB while 65535 and larger are full scale). Treat volume + as effectively on/off for MSTTS. +- **Flextalk per-speaker profile** (applied at voice load, not runtime-adjustable): + values live in `HKLM\Software\AT&T\Watson\2.0\FlexTalk\2.0\Speakers\{GUID}` (see + `docs/NOTES.md` for the full list and defaults). `Pitch`/`Speaking Rate` set the natural + voice; **Expression** was verified to change the synthesis audibly (Keith Bell at + Expression 0 / 59 / 100 yields different waveforms with the same duration). Changing a + profile requires a registry edit and re-selecting the voice. +- **MSTTS voice character** is fixed by the `.vce`/`.cfg` data; the engine exposes no + per-voice profile in the registry. Its effect variants (telephone, in Hall, in Stadium, + in Space) are separate voice modes with separate data, exposed as distinct voice IDs + (`mike-telephone`, `mary-hall`, ...), not runtime effects. +- Not exposed by this integration: SSML, prosody/style tags, or SAPI4 attribute + extensions beyond pitch/speed/volume. + ### Transport and latency - Installation is one command: `setup/install.sh` (system dependencies with sudo, wine diff --git a/host/sapi4host.c b/host/sapi4host.c index dec8518..53048b0 100644 --- a/host/sapi4host.c +++ b/host/sapi4host.c @@ -157,7 +157,7 @@ static void usage(void) fprintf(stderr, "usage: sapi4host -l\n" " sapi4host -v -t -o " - " [-p pitch] [-s speed]\n"); + " [-p pitch] [-s speed] [-V volume]\n"); } /* ------------------------------------------------------------------ */ @@ -168,6 +168,7 @@ int main(int argc, char **argv) { const char *voice = NULL, *text = NULL, *out = NULL; int pitch = -1, speed = -1; + long volume = -1; int i; TTSMODEINFO mode; @@ -182,6 +183,7 @@ int main(int argc, char **argv) else if (!strcmp(argv[i], "-o")) out = argv[++i]; else if (!strcmp(argv[i], "-p")) pitch = atoi(argv[++i]); else if (!strcmp(argv[i], "-s")) speed = atoi(argv[++i]); + else if (!strcmp(argv[i], "-V")) volume = strtol(argv[++i], NULL, 0); else { usage(); return 2; } } if (!voice || !text || !out) { usage(); return 2; } @@ -257,9 +259,9 @@ int main(int argc, char **argv) return 1; } - /* Pitch and speed are optional: when unset, leave the engine's own + /* Pitch, speed and volume are optional: when unset, leave the engine's own voice profile untouched (probing/setting would override it). */ - if (pitch >= 0 || speed >= 0) { + if (pitch >= 0 || speed >= 0 || volume >= 0) { WORD pmin = 0, pmax = 0; DWORD smin = 0, smax = 0; @@ -284,6 +286,13 @@ int main(int argc, char **argv) printf("speed: %d (%lu..%lu)\n", speed, (unsigned long)smin, (unsigned long)smax); } + if (volume >= 0) { + DWORD vdef = 0; + attr->lpVtbl->VolumeGet(attr, &vdef); + attr->lpVtbl->VolumeSet(attr, (DWORD)volume); + printf("volume: %ld (default %lu)\n", + volume, (unsigned long)vdef); + } } {