Как изменить голос, используемый для SAPI.SPVoice

Я хотел бы иметь возможность выбрать альтернативный голос для вывода текста в речь.
Я использую ComObject SAPI.SPVoice, но обнаружил, что не могу изменить фактический используемый голос.
(BTW - Я использую SAPI.SPVoice, потому что он работает как в PowerShell Core, так и в PowerShell Desktop в Windows 10)

${PromptTTS} = New-Object -ComObject SAPI.SPVoice
❯ $PromptTTS | gm
   TypeName: System.__ComObject#{269316d8-57bd-11d2-9eee-00c04f797396}

Name                                   MemberType   Definition
----                                   ----------   ----------
DisplayUI                              Method       void DisplayUI (int hWndParent, string Title, string TypeOfUI, Variant ExtraData)
GetAudioOutputs                        Method       ISpeechObjectTokens GetAudioOutputs (string RequiredAttributes, string OptionalAttributes)
GetVoices                              Method       ISpeechObjectTokens GetVoices (string RequiredAttributes, string OptionalAttributes)
IsUISupported                          Method       bool IsUISupported (string TypeOfUI, Variant ExtraData)
Pause                                  Method       void Pause ()
Resume                                 Method       void Resume ()
Skip                                   Method       int Skip (string Type, int NumItems)
Speak                                  Method       int Speak (string Text, SpeechVoiceSpeakFlags Flags)
SpeakCompleteEvent                     Method       int SpeakCompleteEvent ()
SpeakStream                            Method       int SpeakStream (ISpeechBaseStream Stream, SpeechVoiceSpeakFlags Flags)
WaitUntilDone                          Method       bool WaitUntilDone (int msTimeout)
AlertBoundary                          Property     SpeechVoiceEvents AlertBoundary () {get} {set}
AllowAudioOutputFormatChangesOnNextSet Property     bool AllowAudioOutputFormatChangesOnNextSet () {get} {set}
AudioOutput                            Property     ISpeechObjectToken AudioOutput () {get} {set by ref}
AudioOutputStream                      Property     ISpeechBaseStream AudioOutputStream () {get} {set by ref}
EventInterests                         Property     SpeechVoiceEvents EventInterests () {get} {set}
Priority                               Property     SpeechVoicePriority Priority () {get} {set}
Rate                                   Property     int Rate () {get} {set}
Status                                 Property     ISpeechVoiceStatus Status () {get}
SynchronousSpeakTimeout                Property     int SynchronousSpeakTimeout () {get} {set}
Voice                                  Property     ISpeechObjectToken Voice () {get} {set by ref}
Volume                                 Property     int Volume () {get} {set}
queryMSDNClassInfo                     ScriptMethod System.Object queryMSDNClassInfo();

Мои исследования показывают, что я должен уметь:

❯ $PromptTTS.Voice = ${PromptTTS}.GetVoices().Item(0) ; $PromptTTS.Speak("Hello voice 0")
❯ $PromptTTS.Voice = ${PromptTTS}.GetVoices().Item(1) ; $PromptTTS.Speak("Hello voice 1")
❯ $PromptTTS.Voice = ${PromptTTS}.GetVoices().Item(2) ; $PromptTTS.Speak("Hello voice 2")

и так далее.

Однако, пока команды выполняются без ошибок, используемый / слышимый голос не меняется.


person hugmys0ul    schedule 26.04.2020    source источник
comment
Работает на моей машине, Win10 1909, у меня Хейзел (женщина), Дэвид (мужчина), Зира (другая женщина). Вы можете показать нам результат работы $PromptTTS.GetVoices()?   -  person Mathias R. Jessen    schedule 26.04.2020
comment
@ mathias-r-jessen - это работает в PowerShell Core?   -  person hugmys0ul    schedule 29.04.2020


Ответы (1)


К сожалению, назначение .Voice для изменения голоса не работает в PowerShell Core, с v7.1.0-preview.2 - работает только в Windows PowerShell (версии PowerShell до v5.1).

Поддержка COM в .NET Core ограничена, и хотя PowerShell (Core) частично это компенсирует, есть вещи, которые по-прежнему не работают.

По сути, следующее назначение незаметно игнорируется в PowerShell (Core) 6+:

# !! IGNORED in PowerShell [Core] 6+ - the default voice (David)
# !! is NOT changed (to Hedda).
$PromptTTS.Voice = $PromptTTS.GetVoices().Item(1)

Лично я не знаю обходного пути (по крайней мере, с одним только кодом PowerShell).


Техническая подготовка:

Проверка свойства .Voice с $PromptTTS | Get-Member Voice дает:

   TypeName: System.__ComObject#{269316d8-57bd-11d2-9eee-00c04f797396}

Name  MemberType Definition                                    
----  ---------- ----------                                    
Voice Property   ISpeechObjectToken Voice () {get} {set by ref}

Я подозреваю, что проблема в части set by ref, которая может быть связана со следующей проблемой, процитированной из этого выпуска GitHub:

ComBinder не поддерживается в .NET Core (вызов ComInterop.ComBinder.TryBindSetMember в PowerShell Core - метод-заглушка).

person mklement0    schedule 26.04.2020
comment
mklement0 - Я только что сделал это на моем Win10 PSv5x / PSv7, оба работают, как ожидалось. - person postanote; 27.04.2020
comment
@postanote: Ожидается, что он работает в Windows PowerShell (до v5.1); в PowerShell (Core) 7.0 (и в последней предварительной версии) он не работает - голос не изменяется - по крайней мере, на моей Windows 10 Pro (64-разрядная; Версия 1909, сборка ОС: 18363.778) машина; но для ясности: я не думаю, что проблема связана с конкретной версией Windows 10. - person mklement0; 27.04.2020