Using Windows System.Speech for TTS in PowerShell 7 (is there an alternative?)

Welcome Forums General PowerShell Q&A Using Windows System.Speech for TTS in PowerShell 7 (is there an alternative?)

Viewing 7 reply threads
  • Author
    Posts
    • #220284
      Participant
      Topics: 1
      Replies: 3
      Points: 5
      Rank: Member

      I have the same profile.ps1 in both WindowsPowerShell and PowerShell.
      It includes commands that invoke Windows Text-To-Speech.

      However, these commands fail when run in PowerShell 7.

      The errors occur when I try to use the $PomrptTTS object I create with the following code:

      Add-Type -AssemblyName System.speech
      $PromptTTS = New-Object System.Speech.Synthesis.SpeechSynthesizer

      In PowerShell 7, any attempt to access or use my $PormptTTS object, produces the following:

      SetValueInvocationException: ....\profile.ps1:82
      Line |
        82 |  $PromptTTS.Rate = 0 ; $PromptTTS.Speak("Time for the $((Get-Date).DayofWeek) shuffle")
           |  ~~~~~~~~~~~~~~~~~~~
           | Exception setting "Rate": "Object reference not set to an instance of an object."
      
      MethodInvocationException: ....\profile.ps1:82
      Line |
        82 |   e = 0 ; $PromptTTS.Speak("Time for the $((Get-Date).DayofWeek) shuffle")
           |                                             ~~~~~~~~~~~~~~~~~~~~
           | Exception calling "Speak" with "1" argument(s): "Object reference not set to an instance of an object."

      <p class=”lang-bsh prettyprint prettyprinted”></p>

    • #220314
      Participant
      Topics: 3
      Replies: 340
      Points: 1,120
      Helping Hand
      Rank: Community Hero

      If you’re script works fine in windows powershell you could possibly use this?

      PowerShell 7.0 marks a move a to .NET Core 3.1, enabling significantly more backwards compatibility with existing Windows PowerShell modules. This includes many modules on Windows that require GUI functionality like Out-GridView and Show-Command, as well as many role management modules that ship as part of Windows.

      For Windows, a new switch parameter UseWindowsPowerShell is added to Import-Module. This switch creates a proxy module in PowerShell 7 that uses a local Windows PowerShell process to implicitly run any cmdlets contained in that module. For more information on Import-Module.

      https://docs.microsoft.com/en-us/powershell/scripting/whats-new/what-s-new-in-powershell-70?view=powershell-7

       

    • #220761
      Participant
      Topics: 1
      Replies: 3
      Points: 5
      Rank: Member

      Hi @KrzyDoug,

      I’m afraid I do not follow …

      How does “Add-Type” and “New-Object” correlate to “Import-Module”?

    • #220800
      Participant
      Topics: 3
      Replies: 340
      Points: 1,120
      Helping Hand
      Rank: Community Hero

      I was just thinking you could try to run your existing script that works fine in 5.1 using this, but it may not do what I’m thinking. From what I gather it creates an implicit remoting session to 5.1, so you should have access to properties. However most methods won’t be available. When you say “access $PromptTTS” what types of actions do you take up on it? Either way, it’s just an idea!

    • #220896
      Participant
      Topics: 3
      Replies: 340
      Points: 1,120
      Helping Hand
      Rank: Community Hero

      According to my research, the issue is stated as such

      Beginning in PowerShell 6, ReferencedAssemblies doesn’t include the default .NET assemblies. You must include a specific reference to them in the value passed to this parameter.

      So basically if you want to use the .net assemblies, you’ll need to add them into your powershell session. I was able to do the following and at least got the type of object we were after. Perhaps adding this to the top of your script will fix it.

      
      [Reflection.Assembly]::LoadFile('C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.Speech.dll')
      
      

      Then I was able to run those two commands.

      
      Add-Type -AssemblyName System.speech
      $PromptTTS = New-Object System.Speech.Synthesis.SpeechSynthesizer
      
      

      And get-member showed this

      
      $prompttts | gm
      
      TypeName: System.Speech.Synthesis.SpeechSynthesizer
      
      

      I hope this helps.

    • #222597
      Participant
      Topics: 1
      Replies: 3
      Points: 5
      Rank: Member

      Hi @Doug,

      Yes, I too got that far, BUT, the object created was effectively empty and so unusable

    • #222600
      Participant
      Topics: 1
      Replies: 3
      Points: 5
      Rank: Member

      I have been provided with an answer — by mklement0 at StackOverflow

      >>>

      As of PowerShell 7.0 / .NET Core 3.1, System.Speech.Synthesis.SpeechSynthesizer is considered a .NET Framework-only API and therefore not supported in .NET Core.

      • A discussion about this is ongoing in this GitHub issue; since the underlying API is specific to Windows, the question is whether it’s worth exposing via the cross-platform .NET Core framework.

      The workaround is to use the SAPI.SpVoice COM object (which the .NET Framework implementation is ultimately based on, I presume):

      $sp = New-Object -ComObject SAPI.SpVoice
      $sp.Speak("Time for the $((Get-Date).DayOfWeek) shuffle")

      >>>

       

    • #223413
      Participant
      Topics: 3
      Replies: 340
      Points: 1,120
      Helping Hand
      Rank: Community Hero

      I saw his post and was coming to share it here if needed. Mr. Klement is amazing, I regularly just go to his profiles and view his answers (at various sites) I always learn a ton from his extensive answers. So this workaround directly with the COM object has allowed you to use this in powershell 7?

Viewing 7 reply threads
  • You must be logged in to reply to this topic.