How to Convert Text to Speech with PowerShell
Tags: powershell
Published: 2021-01-14 20:29:52

> _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](http://adamtheautomator.com/author/francisco). Be sure to also check out [more how-to posts on cloud computing, system administration, IT, and DevOps on adamtheautomator.com](https://adamtheautomator.com)!_

<!-- wp:paragraph -->
<p>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?</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="h-prerequisites">Prerequisites</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="h-preparing-text-to-speech-in-powershell">Preparing Text to Speech in PowerShell</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 id="h-adding-the-system-speech-net-assembly">Adding the <code>System.Speech</code> .NET Assembly</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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 <code>System.Speech</code>. The <code>System.Speech</code> assembly is where the &nbsp;<a href="https://docs.microsoft.com/en-us/dotnet/api/system.speech.synthesis.speechsynthesizer">.NET SpeechSynthesizer class</a> lives. &nbsp;This .NET class will allow you to convert text to speech.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><em>The SpeechSynthesizer class is native to the Windows .NET framework. It is not available in PowerShell versions 6+.</em></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>To make the SpeechSythensizer class available, you will load the <code>System.Speeech</code> assembly using the <code>Add-Type</code> cmdlet as shown below.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>When you have a Windows PowerShell console open as administrator, insert the following code snippet.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt; Add-Type -AssemblyName System.Speech</code></pre>
<!-- /wp:code -->

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

<!-- wp:paragraph -->
<p>To ensure the assembly imported, invoke a .NET class that is part of the assembly. For example, since you'll be working with the <code>SpeechSynthesizer</code> class, provide the entire path to the <code>SpeecSynthesizer</code> class enclosed in brackets as shown below.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt; [System.Speech.Synthesis.SpeechSynthesizer]
 
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    SpeechSynthesizer                        System.Object</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>If all is well, you will see the <code>SpeechSynthesizer</code> object returned.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>You have now successfully imported the <code>System.Speech</code> assembly. Next, let's see how you can make the assembly work for you.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 id="h-creating-a-speechsynthesizer-net-object">Creating a <code>SpeechSynthesizer</code> .NET Object</h3>
<!-- /wp:heading -->

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

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt; $ATAVoiceEngine = New-Object System.Speech.Synthesis.SpeechSynthesizer</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>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!</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt; $ATAVoiceEngine

State Rate Volume Voice
----- ---- ------ -----
Ready    0    100 System.Speech.Synthesis.VoiceInfo
</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>If you're curious, you can also take a look at all of the various members on the object by using <code>Get-Member</code>. Note the methods available on this object. You'll be using these in a bit.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt; $ATAVoiceEngine | Get-Member</code></pre>
<!-- /wp:code -->

<!-- wp:heading -->
<h2 id="h-converting-text-to-speech-with-powershell">Converting Text to Speech with PowerShell</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Now that you have an object capable of converting text to speech, let's try it out. To do so, you'll use the <code>Speak()</code> method on the object.</p>
<!-- /wp:paragraph -->

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

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/u50ekm8jfzm2wd019ppt.png)

<!-- wp:paragraph -->
<p>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 <code>Speak()</code> method as shown below.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt; $mytext = "Hello Adam The Automator audience. How are you doing today?"
PS51&gt; $ATAVoiceEngine.Speak($mytext)</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>Feel free to change up the text as much as you want just keep it clean!</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="h-changing-up-the-voice">Changing up the Voice</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>By default, the <code>Speak()</code> method will use a default voice called <a href="https://mssam.fandom.com/wiki/Microsoft_David_(Desktop)#:~:text=Microsoft David (or &quot;Microsoft David,Windows 8 and Windows 10.">Microsoft David</a>. However, you can use many different voices if David doesn't meet your liking.</p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p><em>You might recall that Microsoft David is the default voice in the Windows Narrator feature.</em></p></blockquote>
<!-- /wp:quote -->

<!-- wp:heading {"level":3} -->
<h3 id="h-inspecting-the-voice-object">Inspecting the Voice Object</h3>
<!-- /wp:heading -->

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

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt;  $ATAVoiceEngine | Format-Table -properties *

State Rate Volume Voice
----- ---- ------ -----
Ready    0    100 System.Speech.Synthesis.VoiceInfo
</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>Specifically looking at the <code>Voice</code> property, you can see various attributes as seen below.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt;  $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]...}</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>Let's change the voice that dictates your text.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 id="h-discovering-available-voices">Discovering Available Voices</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Let's see what other voices are available. To get a list of voices, call the <code>GetInstalledVoices()</code> method on the object as shown below.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt; $ATAVoiceEngine.GetInstalledVoices()</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>You will see some <code>VoiceInfo</code> 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.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt; $ATAVoiceEngine.GetInstalledVoices()

VoiceInfo                         Enabled
---------                         -------
System.Speech.Synthesis.VoiceInfo    True
System.Speech.Synthesis.VoiceInfo    True
</code></pre>
<!-- /wp:code -->

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

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS&gt; $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;} 
</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>By referencing the <code>VoiceInfo</code> 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 <code>Microsoft David Desktop</code> and <code>Microsoft Zira Desktop</code> voices available.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt; $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]...}
</code></pre>
<!-- /wp:code -->

<!-- wp:heading {"level":3} -->
<h3 id="h-setting-the-text-to-speech-voice">Setting the Text-to-Speech Voice</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Once you know the available voices, you can then change them by using the <code>SelectVoice()</code> method on the <code>SpeechSynthesizer</code> object. Below you can see how to change the voice to Microsoft Zira Desktop.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt; $ATAVoiceEngine.SelectVoice("Microsoft Zira Desktop")</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>Once the new voice is set, you can then use the <code>Speak()</code> method again passing whatever text you'd like to convert text to speech again using the new voice.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt; $ATAVoiceEngine.Speak("Hello, My name is Zira. PowerShell 7's older sibling?")</code></pre>
<!-- /wp:code -->

<!-- wp:heading -->
<h2 id="h-freeing-up-the-console-while-converting-text-to-speech-with-powershell">Freeing up the Console While Converting Text to Speech with PowerShell</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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 <code>Speak()</code> method, you can use the <code>SpeakAsync()</code> method instead.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The <code>SpeakAsync()</code> method frees up the console as soon as the text-to-speech conversion starts. To use the <code>SpeakAsync()</code> method, simply replace this method with <code>Speak()</code>.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">PS51&gt; $ATAVoiceEngine.SpeakAsync('Hello, my name is Zira. How are you doing today?')</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>You will see that PowerShell frees up the console as soon as you hit Enter.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="h-building-your-own-assistant">Building your Own Assistant</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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 <code>$ATAVoiceEngine</code> 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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code lang="powershell" class="language-powershell">### 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 <a class="wpil_keyword_link" href="https://adamtheautomator.com/powershell-foreach/" title="ForEach" data-wpil-keyword-link="linked">ForEach</a> 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
} </code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:heading -->
<h2 id="h-summary">Summary</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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!</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3 id="h-further-reading">Further Reading:</h3>
<!-- /wp:heading -->

<!-- wp:list -->
<ul><li>Microsoft Docs published a <a href="https://docs.microsoft.com/en-us/dotnet/api/?view=netframework-4.8&amp;term=system.speech">.NET API Browser</a>. It’s a great resource to reference when searching for .NET Assemblies, Namespaces, and Classes.</li><li><a href="https://docs.microsoft.com/en-us/dotnet/api/system.speech.synthesis.speechsynthesizer?view=netframework-4.8">SpeechSynthesizer Class</a></li><li><a href="https://docs.microsoft.com/en-us/dotnet/api/system.speech.synthesis.speechsynthesizer.speak?view=netframework-4.8">SpeechSynthesizer.Speak Method</a></li><li><a href="https://docs.microsoft.com/en-us/dotnet/api/system.speech.synthesis.speechsynthesizer.selectvoice?view=netframework-4.8">SpeechSynthesizer.SelectVoice(String) Method</a></li></ul>
<!-- /wp:list -->