DEV Community

Cover image for How to Convert Text to Speech with PowerShell
Adam the Automator
Adam the Automator

Posted on

How to Convert Text to Speech with PowerShell

This article was written by Francisco Navarro. He is a frequent contributor to the Adam the Automator (ATA) blog. If you'd like to read more from this author, check out his ATA author page. Be sure to also check out more how-to posts on cloud computing, system administration, IT, and DevOps on adamtheautomator.com!

One of PowerShell’s core design principles is to allow the end-user to jump right in with little to no shell experience and hit the ground running. However, PowerShell can let you get creative too. For example, did you know you can convert text to speech with PowerShell?

In this article, you will learn how to have PowerShell speak, switch to available voices, and learn how to use PowerShell's speech capabilities to add notification mechanisms to PowerShell script.

Prerequisites

This article is built as a walkthrough. If you'd like to follow along, please be sure you're running Windows 10. It has everything you need already installed.

Preparing Text to Speech in PowerShell

PowerShell, out of the box, won't just begin talking to you. You'll first have to prepare for it. Doing so requires two steps; adding the correct .NET assembly and creating the appropriate .NET object.

Adding the System.Speech .NET Assembly

Since PowerShell doesn't come with native cmdlets to convert text to speech, you must first dig into .NET using a specific .NET assembly called System.Speech. The System.Speech assembly is where the  .NET SpeechSynthesizer class lives.  This .NET class will allow you to convert text to speech.

The SpeechSynthesizer class is native to the Windows .NET framework. It is not available in PowerShell versions 6+.

To make the SpeechSythensizer class available, you will load the System.Speeech assembly using the Add-Type cmdlet as shown below.

When you have a Windows PowerShell console open as administrator, insert the following code snippet.

PS51> Add-Type -AssemblyName System.Speech

You might have noticed there was no confirmation of any sorts when running the above code. Even by adding the Verbose parameter, you will see no output. Don't worry, everything is fine. This is the default behavior of the Add-Type cmdlet.

To ensure the assembly imported, invoke a .NET class that is part of the assembly. For example, since you'll be working with the SpeechSynthesizer class, provide the entire path to the SpeecSynthesizer class enclosed in brackets as shown below.

PS51> [System.Speech.Synthesis.SpeechSynthesizer]
 
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    SpeechSynthesizer                        System.Object

If all is well, you will see the SpeechSynthesizer object returned.

You have now successfully imported the System.Speech assembly. Next, let's see how you can make the assembly work for you.

Creating a SpeechSynthesizer .NET Object

With the System.Speech assembly, you now have access to the .NET SpeechSynthesizer class. To invoke the class, you need to create an object first. To do that, use the New-Object cmdlet as shown below. The code below is assigning that object to the ATAVoiceEngine variable which you will use in a little bit.

PS51> $ATAVoiceEngine = New-Object System.Speech.Synthesis.SpeechSynthesizer

Once you've created the object and assigned it to a variable, confirm that the variable contains the object you expect. If the output looks like the following code snippet, you're good to go. You're one step closer to converting text to speech in PowerShell!

PS51> $ATAVoiceEngine

State Rate Volume Voice
----- ---- ------ -----
Ready    0    100 System.Speech.Synthesis.VoiceInfo

If you're curious, you can also take a look at all of the various members on the object by using Get-Member. Note the methods available on this object. You'll be using these in a bit.

PS51> $ATAVoiceEngine | Get-Member

Converting Text to Speech with PowerShell

Now that you have an object capable of converting text to speech, let's try it out. To do so, you'll use the Speak() method on the object.

Using Get-Member to inspect the members on the object, notice on method in particular called Speak() as shown below. When you pass text to this method, PowerShell will in turn use .NET to convert that text to speech.

Alt Text

Let's take the default text-to-speech conversion for a spin with all of the default settings. Be sure your sound and speakers are on and have PowerShell convert the phrase "Hello Adam The Automator audience. How are you doing today?" to voice. To do that, simply pass that phrase to the Speak() method as shown below.

PS51> $mytext = "Hello Adam The Automator audience. How are you doing today?"
PS51> $ATAVoiceEngine.Speak($mytext)

Feel free to change up the text as much as you want just keep it clean!

Changing up the Voice

By default, the Speak() method will use a default voice called Microsoft David. However, you can use many different voices if David doesn't meet your liking.

You might recall that Microsoft David is the default voice in the Windows Narrator feature.

Inspecting the Voice Object

Take a look at some of the common properties of the System.Speech.Synthesis.SpeechSynthesizer object. One interesting property is the Voice property. This property allows you to change up the voice used in speech. Notice that this is an embedded object of type System.Speech.Synthesis.VoiceInfo that will have its own set of properties.

PS51>  $ATAVoiceEngine | Format-Table -properties *

State Rate Volume Voice
---------- ---- ------ -----
Ready    0    100 System.Speech.Synthesis.VoiceInfo

Specifically looking at the Voice property, you can see various attributes as seen below.

PS51>  $ATAVoiceEngine.Voice

Gender                : Male
Age                   : Adult
Name                  : Microsoft David Desktop
Culture               : en-US
Id                    : TTS_MS_EN-US_DAVID_11.0
Description           : Microsoft David Desktop - English (United States)
SupportedAudioFormats : {}
AdditionalInfo        : {[Age, Adult], [Gender, Male], [Language, 409], [Name, Microsoft David Desktop]...}

Let's change the voice that dictates your text.

Discovering Available Voices

Let's see what other voices are available. To get a list of voices, call the GetInstalledVoices() method on the object as shown below.

PS51> $ATAVoiceEngine.GetInstalledVoices()

You will see some VoiceInfo objects returned below. You can see that two objects are returned. These objects tell you that two different voices are currently installed. You may have more voices available.

PS51> $ATAVoiceEngine.GetInstalledVoices()

VoiceInfo                         Enabled
--------------                         -------
System.Speech.Synthesis.VoiceInfo    True
System.Speech.Synthesis.VoiceInfo    True

Dig a little bit deeper to find the actual list of voices as properties on each of VoiceInfo objects by passing each of the VoiceInfo objects that the GetInstalledVoices() method returns to Get-Member. Notice the VoiceInfo property's Definition displays the System.Speech.Synthesis.VoiceInfo class and VoiceInfo class.

PS> $ATAVoiceEngine.GetInstalledVoices() | Get-Member

   TypeName: System.Speech.Synthesis.InstalledVoice

Name        MemberType Definition
---------        ---------- ----------
Equals      Method     bool Equals(System.Object obj)
GetHashCode Method     int GetHashCode()
GetType     Method     type GetType()
ToString    Method     string ToString()
Enabled     Property   bool Enabled {get;set;}
VoiceInfo   Property   System.Speech.Synthesis.VoiceInfo VoiceInfo {get;} 

By referencing the VoiceInfo property on each of the objects as shown below, you can get more information about each voice. Below you can see that this computer has Microsoft David Desktop and Microsoft Zira Desktop voices available.

PS51> $ATAVoiceEngine.GetInstalledVoices().VoiceInfo   

Gender                : Male
Age                   : Adult
Name                  : Microsoft David Desktop
Culture               : en-US
Id                    : TTS_MS_EN-US_DAVID_11.0
Description           : Microsoft David Desktop - English (United States)
SupportedAudioFormats : {}
AdditionalInfo        : {[Age, Adult], [Gender, Male], [Language, 409], [Name, Microsoft David Desktop]...}

Gender                : Female
Age                   : Adult
Name                  : Microsoft Zira Desktop
Culture               : en-US
Id                    : TTS_MS_EN-US_ZIRA_11.0
Description           : Microsoft Zira Desktop - English (United States)
SupportedAudioFormats : {}
AdditionalInfo        : {[Age, Adult], [Gender, Female], [Language, 409], [Name, Microsoft Zira Desktop]...}

Setting the Text-to-Speech Voice

Once you know the available voices, you can then change them by using the SelectVoice() method on the SpeechSynthesizer object. Below you can see how to change the voice to Microsoft Zira Desktop.

PS51> $ATAVoiceEngine.SelectVoice("Microsoft Zira Desktop")

Once the new voice is set, you can then use the Speak() method again passing whatever text you'd like to convert text to speech again using the new voice.

PS51> $ATAVoiceEngine.Speak("Hello, My name is Zira. PowerShell 7's older sibling?")

Freeing up the Console While Converting Text to Speech with PowerShell

You may have noticed when passing text to the speech engine that PowerShell does not allow you to do anything else while speech is happening. This may be problematic if you need to recite a lot of text and still need to perform some other tasks. Instead of using the Speak() method, you can use the SpeakAsync() method instead.

The SpeakAsync() method frees up the console as soon as the text-to-speech conversion starts. To use the SpeakAsync() method, simply replace this method with Speak().

PS51> $ATAVoiceEngine.SpeakAsync('Hello, my name is Zira. How are you doing today?')

You will see that PowerShell frees up the console as soon as you hit Enter.

Building your Own Assistant

So far you have instantiated a .NET class and turned it into a Windows PowerShell object you can leverage. You had some fun with the $ATAVoiceEngine object and learned to manipulate its settings, including changing the voice. Now let's leverage the object to be a notification mechanism in your PowerShell Scripts.

By combining a typical PowerShell script with the text-to-speech ability, your computer can vocally tell you what's going on! Below is a good example. When you run this script the engine calls out the name of the service and its status.

### In honor of PowerShell 7's avatar we select the "Zira Desktop Voice"  
$ATAVoiceEngine.SelectVoice("Microsoft Zira Desktop")

### Fetching a list of Windows Services which names start with "W" selecting the first 5
$services = Get-Service W* | Select-Object -First 5

### Create a ForEach Loop for those services
ForEach ($service in $services) {
    ### Using the speech engine object $ATAVoiceEngine with the "SpeakAsync" method
    $ATAVoiceEngine.SpeakAsync("The Service $($service.displayname) is $($service.status)") | Out-Null
} 

You can run a script like this in the background, against a server or workstations and find out if your critical services are running or not.

Summary

In this article, you've discovered how to use the .NET framework and PowerShell to convert text to speech by changing up various voices and how to even integrate text-to-speech into your PowerShell scripts. Go ahead, get creative, and build your own PowerShell assistant!

Further Reading:

Top comments (2)

Collapse
 
ibitzee profile image
IBIT.ZEE • Edited

dude--- that do not work with Powershell 7.1.2
the library is loaded...
the object is created...
but when you use the speak method you get this error:

"ParentContainsErrorRecordException: Exception calling "Speak" with "1" argument(s): "
Object reference not set to an instance of an object."
Enter fullscreen mode Exit fullscreen mode

?any ideas??

Collapse
 
sprawdzoneit profile image
sprawdzone.it

how to add voice language speak online for example with powershell?

DISM /Online /Add-Package /language pl-PL.cab ?????