Feb 242014
 
 February 24, 2014  Posted by at 9:53 am PowerShell Tagged with:  Add comments

Using Text-To-Speech in scripts

I have some scripts that take some time to run for my virtual machines, so I’ve added speech so I’ll know when they are done. Just as I use speech a fair bit on my phone (having my messages read out and replying) I also use it on my computer.

The black garlic ice cream script I tweeted a few weeks back seemed popular, in case you missed it you can download it here. It started as a joke, but if combined with speech recognition could be a fun way to get some help in the kitchen?

clip_image001

Using text to speech is very straightforward, and the reason is because PowerShell lets us leverage objects- and we can access both COM and .Net objects.

Calling objects or cmdlts?

When you have the choice between the two, I personally use the cmdlt equivalent if it does the job for me for readability reasons. It doesn’t however make much of a difference. Except if we are talking about a lot of iterations and performance is important. I did some tests with Get-Process and added a member enumeration on top of it. I also tested with other objects, and on average there was a difference,- with the static call being a little bit faster. But, I’m not sure it matters for most of us.

clip_image003

So before you go ahead and grab those objects, or create your own, make sure you check if there is a cmdlt, and then decide on what you want to do.

Searching for cmdlts

If you wonder how to look for cmdlts you have several options. My two favorites are, if in the ISE I simply use the search panel.

clip_image004

If you aren’t using the ISE, then you can use the Get-Command, and do a query using wildcards (for example).

clip_image006

Using COM and .Net objects in PowerShell

Speech is a good example, because you can do text to speech in two ways. The old way, COM, or the .Net way using the Speech API.

Both need the New-Object cmdlt. This creates a new instance of the object. If it’s a –com object you pass that in after the New-Object.

When you create an instance of a .Net object you need to add the assembly, and that is done with Add-Type. Add-Type adds a class to the PowerShell session. This means that you can also create your own types and add them, either by writing up the class in in the script file- or reading it in.

Let’s compare the two ways with text to speech.

 
[sourcecode language=”javascript”]
# ‘Old’ way using COM
function Voice-By-COM([string]$str){
$voice = new-object -com SAPI.SpVoice
$voice.Speak($str, 2)
}

# Using Speech API
function Talk-To-Me([string]$str){
Add-Type -AssemblyName System.speech
$voice = New-Object System.Speech.Synthesis.SpeechSynthesizer
$voice.SelectVoice(‘Microsoft Zira Desktop’)
$voice.Speak($str)
}
[/sourcecode]

clip_image008

A function call in PowerShell would simply be Talk-To-Me “What to say”

Creating your own C# objects

If you want to create your own type you either read in the file, or write up the class in the script file, pass it in to the Add-Type cmdlt.

[sourcecode language=”javascript”]
Add-Type @"

public class MyClass
{
public static string SayHiStatic(string name)
{
return ("Hello " + name);
}

public string SayHi(string name)
{
return ("Hello " + name);
}
}
"@
#Static call
[MyClass]::SayHiStatic("Iris")

#New object call
$obj = New-Object MyClass
$obj.SayHi("Iris")
[/sourcecode]

clip_image009

Static methods can be called using the [ClassName]::Method(param) syntax, otherwise you would have to use the New-Object cmdlt and create a new object of your class type. You can then use it, and you will even get intellisense support and tab completion.

clip_image010

The problem is, you often need to reference an assembly or more. This means that you need to add the assemblies manually, how would it otherwise know where to find them?

Referencing assemblies for custom types

Let’s change up the code. Say that we wanted to use the Speech API in our class, and actually say hi.

clip_image012

This means we need a reference to System.Speech.dll.

clip_image014

Add-Type has a parameter called –ReferencedAssemblies

If you were to run:

Get-Help Add-Type -Parameter ReferencedAssemblies

You would see that:

-ReferencedAssemblies <String[]>

Specifies the assemblies upon which the type depends. By default, Add-Type references System.dll and System.Management.Automation.dll. The assemblies that you specify by using this parameter are referenced in addition to the default assemblies.

This means that we can do this:

clip_image016

 

[sourcecode language=”javascript”]
$speechCode = @"

public class SpeechClass
{
public static void SayHiStatic(string name)
{
new System.Speech.Synthesis.SpeechSynthesizer().Speak("Hello " + name);
}
}
"@

$assemblies = @(
"c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1\System.Speech.dll")

Add-Type -ReferencedAssemblies $assemblies -TypeDefinition $speechCode

#Static call
[SpeechClass]::SayHiStatic("Iris")
[/sourcecode]

For brevity I’ve used a static method- you can of course do as you want.

Summary

You can use and create your own objects in PowerShell, and the two most important cmdlts to remember are Add-Type and New-Object. Remember to also reference assemblies needed for your own custom types.

When given the choice between a cmdlt or cerating and calling an object always use the cmdlt

Here is an example using Text-To-Speech wit COM, .Net call in PowerShell, and custom type that uses the Speech API. There is also a simple example of newing up objects or doing static method calls.

[sourcecode language=”javascript”]
# ‘Old’ way using COM
function Voice-By-COM([string]$str){
$voice = new-object -com SAPI.SpVoice
$voice.Speak($str, 2)
}

# Using Speech API
function Talk-To-Me([string]$str){
Add-Type -AssemblyName System.speech
$voice = New-Object System.Speech.Synthesis.SpeechSynthesizer
$voice.SelectVoice(‘Microsoft Zira Desktop’)
$voice.Speak($str)
}

$speechCode = @"

public class SpeechClass
{
public static void SayHiStatic(string name)
{
new System.Speech.Synthesis.SpeechSynthesizer().Speak("Hello " + name);
}
}
"@

$assemblies = @(
"c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1\System.Speech.dll")

Add-Type -ReferencedAssemblies $assemblies -TypeDefinition $speechCode

#Static call
[SpeechClass]::SayHiStatic("Iris")

Add-Type @"

public class MyClass
{
public static string SayHiStatic(string name)
{
return ("Hello " + name);
}

public string SayHi(string name)
{
return ("Hello " + name);
}
}
"@
#Static call
[MyClass]::SayHiStatic("Iris")

#New object call
$obj = New-Object MyClass
$obj.SayHi("Iris")

# This
([diagnostics.process]::GetProcesses()).Name

# Or better, this
Get-Process

# Search for commands
Get-Command *proc*

[/sourcecode]

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)

What is 12 + 14 ?
Please leave these two fields as-is:
IMPORTANT! To be able to proceed, you need to solve the following simple math (so we know that you are a human) :-)