|
Saturday, February 04, 2006
Give Monad a Voice: out-voice cmdlet
WindowsXP shipped with both speech recognition and Text To Speech Engine. TTS engine (sapi.dll) takes text as input and output that text as spoken audio. Although WinFX will contain the fully managed API for speech, at this moment, I have to use (ugly) com interop. To make things easier, I generate a managed API libaray (SpeechLib.dll) using tlbimp.exe, a tool in .NET 2.0 SDK.
set-alias tlbimp "C:\Program Files\Microsoft.NET\SDK\v2.0\bin\tlbimp.exe"You will see some warnings talking about type error. Just ignore them because we will not use those problemetic class today. Then we can load the libray, initialize a SpeechLib.SpVoiceClass object and call Speak method.
tlbimp "C:\Program Files\Common Files\Microsoft Shared\Speech\sapi.dll" `
/out:SpeechLib.dll /namespace:SpeechLib
[Reflection.Assembly]::LoadFile("D:\msh\SpeechLib.dll")After using this cmdlet for a while, I found out that call Speak method asynchronously is a good idea. While listen to your voice output (which could be lengthy and boring), you can still get a prompt and working with MSH.
$SpVoice = new-object SpeechLib.SpVoiceClass
$SpVoice.Speak("Hello", [SpeechLib.SpeechVoiceSpeakFlags]::SVSFDefault)
$SpVoice.Speak("Hello", [SpeechLib.SpeechVoiceSpeakFlags]::SVSFlagsAsync)To get a .wav filename, I used System.Windows.Forms.SaveFileDialog object
$SaveFileD = new-object System.Windows.Forms.SaveFileDialogTo save to wav file, I use SpeechLib.SpFileStreamClass
$SaveFileD.Filter = "All files (*.*)|*.*|wav files (*.wav)|*.wav";
$SaveFileD.Title = "Save to a wave file";
$SaveFileD.FilterIndex = 2;
$SaveFileD.RestoreDirectory = true;
if ($SaveFileD.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK)
{
$FileName = $SaveFileD.FileName
}
$WaveFile = new-object SpeechLib.SpFileStreamClassI use $_ to get object from pipline and use out-string cmdlet to convert them to System.String object .
$WaveFile.Open($FileNameS, [SpeechLib.SpeechStreamFileMode]::SSFMCreateForWrite, $false)
$SpVoice.AudioOutputStream = $WaveFile
$SpVoice.Speak($StringToSay, [SpeechLib.SpeechVoiceSpeakFlags]::SVSFlagsAsync)
$SpVoice.WaitUntilDone([System.Threading.Timeout]::Infinite)
$WaveFile.Close()
$StringToSay = $_ | out-stringIf you want to try it, do something like
dir| select-object Name -first 4 | .\out-voice.mshMake sure the speaker on your computer is poweron and enabled. Make sure you use select to format your results first. Otherwise you won't know what your computer are talking about. There is no guarantee that it would be fun. As I said before it could be lengthy and boring.
get-eventlog Application -Newest 4 | .\out-voice.msh $true
###############################################################Reference: Code4Fun, C# Corner
#
# out-voice.msh
# This script takes objects from pipline and output as voice using TTS engine.
# If set $SaveToWav to $true, output to WAV file(s).
#
# Usage: "Hello", "Welcome to monad world" | out-voice
# "Hello", "Welcome to monad world" | out-voice $true
#
# from Tony http://mshforfun.blogspot.com/
#
################################################################
param ( [bool] $SaveToWav = $false )
begin
{
[void][Reflection.Assembly]::LoadFile("D:\msh\SpeechLib.dll")
$SpVoice = new-object SpeechLib.SpVoiceClass
$count = 0
if ($SaveToWav)
{
[void] [Reflection.Assembly]::Load("System.Windows.Forms, Version=2.0.0.0, `
Culture=neutral, PublicKeyToken=b77a5c561934e089")
$SaveFileD = new-object System.Windows.Forms.SaveFileDialog
$SaveFileD.Filter = "All files (*.*)|*.*|wav files (*.wav)|*.wav";
$SaveFileD.Title = "Save to a wave file";
$SaveFileD.FilterIndex = 2;
$SaveFileD.RestoreDirectory = true;
if ($SaveFileD.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK)
{
$FileName = $SaveFileD.FileName
}
else
{
"Must Specify a wav file"
break
}
}
}
process
{
if ($_)
{
$count++
$StringToSay = $_ | out-string
"$count: $StringToSay"
if ($SaveToWav)
{
$FileNameS = $FileName.Replace(".wav", $count.Tostring()+".wav")
$WaveFile = new-object SpeechLib.SpFileStreamClass
$WaveFile.Open($FileNameS, `
[SpeechLib.SpeechStreamFileMode]::SSFMCreateForWrite, $false)
$SpVoice.AudioOutputStream = $WaveFile
[void]$SpVoice.Speak($StringToSay, `
[SpeechLib.SpeechVoiceSpeakFlags]::SVSFlagsAsync)
[void]$SpVoice.WaitUntilDone([System.Threading.Timeout]::Infinite)
$WaveFile.Close()
}
else
{
[void]$SpVoice.Speak($StringToSay, `
[SpeechLib.SpeechVoiceSpeakFlags]::SVSFlagsAsync)
}
}
else
{
"null object"
}
}
end
{
"Number of objects successfully output: $count"
}
##############################################
Have Fun!
[Edit: Monad has now been renamed to Windows PowerShell. This script or discussion may require slight adjustments before it applies directly to newer builds.]
Tags: msh monad PowerShell
Post a Comment