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.
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:
- Microsoft Docs published a .NET API Browser. It’s a great resource to reference when searching for .NET Assemblies, Namespaces, and Classes.
- SpeechSynthesizer Class
- SpeechSynthesizer.Speak Method
- SpeechSynthesizer.SelectVoice(String) Method
Top comments (2)
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:
?any ideas??
how to add voice language speak online for example with powershell?
DISM /Online /Add-Package /language pl-PL.cab ?????