In science-fiction films, someone walks into a dark room and says, “Computer, lights 50%!” “Lights 50% acknowledged!” the computer answers in a pleasant female contralto, turning on a dim light. Who has not wanted that?
In Robert Heinlein’s novel The Moon Is a Harsh Mistress (1966), the supercomputer Mike could speak freely, but it took up an enormous amount of space and certainly was not mobile. My Asus EeePC 1000 netbook could not hold a conversation, but I wanted to hear what it had to say.
In May 2009, I had just installed Windows 7 RC on it. The PowerShell console and PowerShell ISE interested me most. There were third-party cmdlets for speech playback, but I wanted an ordinary function I could carry around in my profile without installing extra commands.
The ast prefix came from one of my nicknames. Typing those three characters made my homegrown functions easy to find with autocomplete.
The Function I Used
The function below is preserved from that experiment, along with its example calls. It used SAPI COM for speech and Windows Forms for clipboard access. Its LoadWithPartialName call is now marked obsolete in the .NET documentation.
Modern PowerShell has a built-in Get-Clipboard command. The original post mentioned its disappearance from an unspecified build; without that build’s details, that claim cannot be verified. The Windows Forms code below records the approach I used in 2009.
function astSpeak(
[string]$inputString,
[int]$speed = -2,
[int]$engine = 0,
[switch]$file,
[switch]$list,
[switch]$buffer,
[int]$volume = 85
) {
# Create the object
$oVoice = New-Object -com "SAPI.spvoice"
# If a list of voices is requested
if($list)
{
Write-Output "Voices installed in the system: "
$i = 0
Foreach ($Token in $oVoice.getvoices())
{
Write-Host $i - $Token.getdescription()
$i++
}
}
# If speech output is requested
else
{
# Get text from a file if the switch is set
if($file){ $toSpeechText = Get-Content $inputString}
# Speak text from the clipboard (requires STA mode)
elseif($buffer){
$null = [reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$toSpeechText = [Windows.Forms.Clipboard]::GetText() }
# Use the received string if no switch is set
else{ $toSpeechText = $inputString}
# Play it
$oVoice.rate = $speed
$oVoice.volume = $volume
$oVoice.voice = $oVoice.getvoices().item($engine)
$oVoice.Speak($toSpeechText)
}
}Trying the Voices
I had Alyona installed as my main voice. She read Wikipedia articles and fiction quite successfully. For the latter, I used Harry Harrison’s short ironic story “The Ultimate Weapon,” taken from Moshkov’s Library. Listening exposed several typos and made me appreciate the Russian letter “yo”: leaving its dots out confused the pronunciation. Otherwise, the reading was impressive.
This call listed the voices installed on the machine:
astSpeak -l
Plain text was the simplest case:
astSpeak "Hello! My name is Alyona, I am the voice of your computer!"For a text file, I supplied the file switch and a path:
astSpeak -f "D:\Library\Rudyard Kipling\If.txt"Yes… perhaps we will not trust Alyona with poetry anymore :)
Clipboard access needed more care. The Windows Forms clipboard API required an STA (single-threaded apartment) thread. The Windows PowerShell 2.0 console started in MTA by default, so I added -sta to its shortcut:

The original post also described ISE as using MTA. That was incorrect: the PowerShell team’s April 2009 comparison identifies ISE as STA. The console’s default changed to STA in Windows PowerShell 3.0.
I then used the buffer switch to read the clipboard:
astSpeak -bThe scariest thing was finding a URL in there.
The playback calls also accepted a speed, volume and voice index. On that machine, Katerina was voice number 1. This example used her voice at volume 85/100 and speed 3:
astSpeak -e 1 -v 85 -s 3 "Hello"





