OutSpeech.psm1
#Requires -Version 5.1 ############################################################################################### # Module Variables ############################################################################################### $ModuleVariableNames = ('OutSpeechConfiguration', 'SpeechConfigurations') $ModuleVariableNames.ForEach( { Set-Variable -Scope Script -Name $_ -Value $null }) ############################################################################################### # Module Removal ############################################################################################### #Clean up objects that will exist in the Global Scope due to no fault of our own . . . like PSSessions $OnRemoveScript = { # perform cleanup Write-Verbose -Message 'Removing Module Items from Global Scope' } $ExecutionContext.SessionState.Module.OnRemove += $OnRemoveScript Function Enable-SpeechConfiguration { <# .SYNOPSIS Creates a SpeechConfiguration (System.Speech.Synthesis.SpeechSynthesizer object) with the specified values .DESCRIPTION Creates a SpeechConfiguration (System.Speech.Synthesis.SpeechSynthesizer object) with the specified values for Rate, Volume, and Voice. SpeechConfiguration objects are stored in the module variable SpeechConfigurations and can be retrieved, set, or deleted using the Get-SpeechConfiguration, Set-SpeechConfiguration, or Disable-SpeechConfiguration functions. .EXAMPLE PS C:\Enable-SpeechConfiguration -Rate 3 -Volume 50 -Voice 'Microsoft David Desktop' -ConfigurationName 'DavidR3V50' .PARAMETER Rate Specifies the speech rate with higher values being faster. Valid range is -10 through 10. Default is 0. .PARAMETER Volume Specifies the speech volume with higher values being louder. Valid range is 1 through 100. Default is 100. .PARAMETER Voice Specifies the speech voice to use. Run Get-SpeechVoice to see valid values - use the name attribute. Default depends on the system language/culture settings. .PARAMETER ConfigurationName Specifies the ConfigurationName to create. Default is 'Default'. #> [cmdletbinding()] param ( [parameter()] [string]$ConfigurationName = 'Default' , [parameter()] [string]$Voice , [parameter()] [ValidateRange(-10, 10)] [Int]$Rate , [parameter()] [ValidateRange(1, 100)] $Volume ) if ($Script:SpeechConfigurations.ContainsKey($ConfigurationName) -and $ConfigurationName -ne 'Default') { throw ("SpeechConfiguration $ConfigurationName already exists. Use Disable-SpeechConfiguration to remove it or Set-SpeechConfiguration to modify it.") Return } $InitializeSpeechParams = @{ ErrorAction = 'Stop' ConfigurationName = $ConfigurationName } foreach ($param in $PSBoundParameters.Keys) { if ($param -in 'Rate', 'Volume', 'Voice', 'Verbose') { $InitializeSpeechParams.$param = $($PSBoundParameters.$param) } } Initialize-Speech @InitializeSpeechParams }#function Enable-Speech Function Disable-SpeechConfiguration { <# .SYNOPSIS Disables (deletes) a SpeechConfiguration from the OutSpeech module's SpeechConfigurations variable. .DESCRIPTION Disables (deletes) a SpeechConfiguration from the OutSpeech module's SpeechConfigurations variable. .EXAMPLE PS C:\> Enable-SpeechConfiguration -ConfigurationName 'TooFastTooLoud' -Rate 10 -Volume 100 PS C:\> Get-SpeechConfiguration -ConfigurationName 'TooFastTooLoud' State Rate Volume Voice ----- ---- ------ ----- Ready 10 100 System.Speech.Synthesis.VoiceInfo PS C:\> Disable-SpeechConfiguration -ConfigurationName 'TooFastTooLoud' PS C:\> Get-SpeechConfiguration -ConfigurationName 'TooFastTooLoud' # No output expected .PARAMETER ConfigurationName The name of an existing SpeechConfiguration to Disable (delete). .PARAMETER All Specifying All causes Disable-SpeechConfiguration to Disable (delete) all currently configured SpeechConfigurations. #> [cmdletbinding(DefaultParameterSetName = 'NamedConfig')] param ( [Parameter(ParameterSetName = 'NamedConfig')] [string[]]$ConfigurationName , [Parameter(ParameterSetName = 'All')] [switch]$All ) switch ($PSCmdlet.ParameterSetName) { 'NamedConfig' { foreach ($cn in $ConfigurationName) { if ($Script:SpeechConfigurations.ContainsKey($cn)) { $Script:SpeechConfigurations.$cn.dispose() $Script:SpeechConfigurations.remove($cn) } Else { Write-Warning "SpeechConfiguration $cn does not exist." } } } 'All' { $Keys = $Script:SpeechConfigurations.Keys | ForEach-Object { $_ } #disconnect the Keys from the actual hashtable object foreach ($k in $Keys) { $Script:SpeechConfigurations.$k.dispose() $Script:SpeechConfigurations.remove($k) } } } }#Function Disable-SpeechConfiguration Function Set-SpeechConfiguration { <# .SYNOPSIS Modifies a SpeechConfiguration (System.Speech.Synthesis.SpeechSynthesizer object) with the specified values .DESCRIPTION Modifies a SpeechConfiguration (System.Speech.Synthesis.SpeechSynthesizer object) with the specified values for Rate, Volume, and Voice. SpeechConfiguration objects are stored in the module variable SpeechConfigurations and can be retrieved, created, or deleted using the Get-SpeechConfiguration, Enable-SpeechConfiguration, or Disable-SpeechConfiguration functions. .EXAMPLE PS C:\Enable-SpeechConfiguration -Voice 'Microsoft David Desktop' -ConfigurationName 'DavidR3V50' PS C:\Set-SpeechConfiguration -Rate 3 -Volume 50 -Voice 'Microsoft David Desktop' -ConfigurationName 'DavidR3V50' Modifies the Speech Configuration 'DavidR3V50' with the specifed Rate, Volume, and Voice values. .PARAMETER Rate Specifies the speech rate with higher values being faster. Valid range is -10 through 10. Default is 0. .PARAMETER Volume Specifies the speech volume with higher values being louder. Valid range is 1 through 100. Default is 100. .PARAMETER Voice Specifies the speech voice to user. Run Get-SpeechVoice to see valid values - use the name attribute. Default depends on the system language/culture settings. .PARAMETER ConfigurationName Specifies the ConfigurationName to modify. Default is 'Default'. #> [cmdletbinding()] param ( [parameter()] $ConfigurationName = 'Default' , [parameter()] [string]$Voice , [parameter()] [ValidateRange(-10, 10)] [Int]$Rate , [parameter()] [ValidateRange(1, 100)] $Volume ) if ($script:SpeechConfigurations.ContainsKey($ConfigurationName)) { $SpeechConfiguration = $script:SpeechConfigurations.$ConfigurationName } else { throw ("SpeechConfiguration $ConfigurationName does not exist. Use Enable-SpeechConfiguration to create it.") Return } If ($PSBoundParameters.ContainsKey('Volume')) { Write-Verbose "Setting SpeechConfiguration $ConfigurationName volume to $Volume" $SpeechConfiguration.Volume = $Volume } Else { Write-Verbose "SpeechConfiguration $ConfigurationName volume = $($SpeechConfiguration.volume)" } If ($PSBoundParameters.ContainsKey('Rate')) { Write-Verbose "Setting SpeechConfiguration $ConfigurationName rate to $Rate" $SpeechConfiguration.Rate = $Rate } Else { Write-Verbose "SpeechConfiguration $ConfigurationName rate = $($SpeechConfiguration.rate)" } If ($PSBoundParameters.ContainsKey('Voice')) { Write-Verbose "Setting SpeechConfiguration voice to $Voice" $SpeechConfiguration.SelectVoice($Voice) } Else { Write-Verbose "SpeechConfiguration $ConfigurationName voice = $($SpeechConfiguration.voice.name)" } } Function Export-Speech { <# .SYNOPSIS Exports the specified input text or objects (converting them to a string with out-string) to a specified Wave file. .DESCRIPTION Exports the specified input text or objects (converting them to a string with out-string) to a specified Wave file using the Path parameter. A SpeechConfiguration can be configured with the ConfigurationName parameter or the rate, volume, or voice parameters can be used to adjust the output. .EXAMPLE PS C:\> Enable-SpeechConfiguration -ConfigurationName 'TooFastTooLoud' -Rate 10 -Volume 100 PS C:\> Export-Speech -Path MyWaveFile.wav -ConfigurationName 'TooFastTooLoud' -InputObject "Here is a sentence read by the computer." PS C:\> Get-Item MyWaveFile.wav PS C:\> Invoke-Item MyWaveFile.wav .PARAMETER InputObject Strings or objects that will be converted to speech and sent to the default audio device. .PARAMETER Path Specify a valid path to a wav file to store the audio output export. File does not need to exist but an existing file will be clobbered without warning. .PARAMETER Rate Specifies the speech rate with higher values being faster. Valid range is -10 through 10. Default is 0. If an existing SpeechConfiguration is specified with the ConfigurationName parameter this will modify that SpeechConfiguration's rate value. .PARAMETER Volume Specifies the speech volume with higher values being louder. Valid range is 1 through 100. Default is 100. If an existing SpeechConfiguration is specified with the ConfigurationName parameter this will modify that SpeechConfiguration's volume value. .PARAMETER Voice Specifies the speech voice to use. Run Get-SpeechVoice to see valid values - use the name attribute. Default depends on the system language/culture settings. If an existing SpeechConfiguration is specified with the ConfigurationName parameter this will modify that SpeechConfiguration's voice. .PARAMETER ConfigurationName Specifies the ConfigurationName to use. Default is 'Default'. If Rate, Volume, or Voice are specified the associated SpeechConfiguration's value for that attribute will be modified. #> [cmdletbinding()] param ( [Parameter(ValueFromPipeline = 'True')] [string[]]$inputobject , [Parameter()] $ConfigurationName = 'Default' , [Parameter()] [string]$voice , [Parameter()] [ValidateRange(-10, 10)] [Int]$Rate , [Parameter()] [ValidateRange(1, 100)] $Volume , [Parameter(Mandatory = $true)] [string]$Path ) begin { $SpeechParams = @{ ErrorAction = 'Stop' ConfigurationName = $ConfigurationName } foreach ($param in $PSBoundParameters.Keys) { if ($param -in 'Rate', 'Volume', 'Voice', 'Verbose') { $SpeechParams.$param = $($PSBoundParameters.$param) } } switch ($script:SpeechConfigurations.ContainsKey($ConfigurationName)) { $true { #set Set-SpeechConfiguration @SpeechParams } $false { #enable Enable-SpeechConfiguration @SpeechParams } } $SpeechConfiguration = $script:SpeechConfigurations.$ConfigurationName Write-Verbose "Setting speech to output to wavfile: $Path" $SpeechConfiguration.SetOutputToWaveFile($Path) }#begin Process { ForEach ($line in $inputobject) { Write-Verbose "Exporting: $line" $null = $SpeechConfiguration.Speak(($line | Out-String)) } }#Process End { $SpeechConfiguration.SetOutputToNull() $SpeechConfiguration.SetOutputToDefaultAudioDevice() } }#Function Export-Speech function Get-SpeechConfiguration { <# .SYNOPSIS Gets all existing SpeechConfigurations or those that match the values provided with the ConfigurationName parameter. .DESCRIPTION Gets all existing SpeechConfigurations or those that match the values provided with the ConfigurationName parameter. .PARAMETER ConfigurationName Specify the name (string) of an existing SpeechConfiguration to get. 'Default' should always exist unless it has been disabled with Disable-SpeechConfiguration. .PARAMETER Rate Specifies the speech rate of the configurations to return. Valid range is -10 through 10. .PARAMETER Volume Specifies the speech volume of the configurations to return. Valid range is 1 through 100. .PARAMETER Voice Specifies the speech voice of the configurations to return. Run Get-SpeechVoice to see valid values - use the name attribute. Default depends on the system language/culture settings. .EXAMPLE PS C:\> Enable-SpeechConfiguration -ConfigurationName 'Test' PS C:\> Get-SpeechConfiguration Name Value ---- ----- Test System.Speech.Synthesis.SpeechSynthesizer Default System.Speech.Synthesis.SpeechSynthesizer In this example you see Get-SpeechConfiguration returning both the Default SpeechConfiguration and the Test SpeechConfiguration. .EXAMPLE PS C:\> Enable-SpeechConfiguration -ConfigurationName 'Test' PS C:\> Get-SpeechConfiguration Name Value ---- ----- Test System.Speech.Synthesis.SpeechSynthesizer In this example you see Get-SpeechConfiguration returning the Test SpeechConfiguration. #> [CmdletBinding()] param ( [parameter()] [string[]]$ConfigurationName , [parameter()] [string[]]$Voice , [parameter()] [ValidateRange(-10, 10)] [Int[]]$Rate , [parameter()] [ValidateRange(1, 100)] [int[]]$Volume ) $Script:SpeechConfigurations.keys.foreach( { $Script:SpeechConfigurations.$_ | Add-Member -MemberType NoteProperty -Name ConfigurationName -Value $_ -PassThru -Force }).where( { ($null -eq $ConfigurationName -or $_.ConfigurationName -in $ConfigurationName) -and ($null -eq $Voice -or $_.Voice.Name -in $Voice) -and ($null -eq $Rate -or $_.Rate -in $Rate) -and ($null -eq $Volume -or $_.Volume -in $Volume) } ) } Function Get-SpeechVoice { <# .SYNOPSIS Gets all or specified currently available voices that can be specified for use with SpeechConfigurations, Out-Speech, or Export-Speech .DESCRIPTION Gets all or specified currently available voices that can be specified for use with SpeechConfigurations, Out-Speech, or Export-Speech. Voices can be specified by name, age, gender, or culture. Get more voices here: https://www.microsoft.com/en-us/download/details.aspx?id=27224 .PARAMETER Name Specify the name of the voice to get. Simple wildcards supported. Run 'Get-SpeechVoice | Select-Object -ExpandProperty Name' to see all available names. .PARAMETER VoiceID Specify the ID of voice to get, like 'TTS_MS_EN-US_DAVID_11.0' Simple wildcards supported. Run 'Get-SpeechVoice | Select-Object -ExpandProperty ID' to see all available IDs. .PARAMETER Age Specify the age of the voice to get. Valid values are 'Adult','Child','NotSet','Senior','Teen' .PARAMETER Gender Specfiy the gender of the voice to get. Valid values are 'Female','Male','Neutral','NotSet' .PARAMETER Culture Specify the culture of the voice to get. Simple wildcards supported. Like 'en-*' or 'es-*' .EXAMPLE PS C:\> Get-SpeechVoice Gets all currently available voices .EXAMPLE PS C:\> Get-SpeechVoice -Name 'Microsoft Zira Desktop' Gets the voice with the name specified if it is available on the system. .EXAMPLE PS C:\> Get-SpeechVoice -Name '*Desktop' Gets the available voices with matching names. .EXAMPLE PS C:\> Get-SpeechVoice -Culture en-* Gets the available voices with a matching culture. .EXAMPLE PS C:\> Get-SpeechVoice -Age Adult Gets the available voices with a Adult age designation. .EXAMPLE PS C:\> Get-SpeechVoice -Gender Female Gets the available voices with a Female gender designation. #> [cmdletbinding()] param ( [parameter()] [string[]]$VoiceId , [parameter()] [string[]]$Name , [parameter()] [validateset('Adult', 'Child', 'NotSet', 'Senior', 'Teen')] [string[]]$Age , [parameter()] [validateset('Female', 'Male', 'Neutral', 'NotSet')] [string[]]$Gender , [parameter()] [string[]]$Culture # like 'en-US' ) if (-not $script:SpeechConfigurations.containskey('Default')) { Enable-SpeechConfiguration -ConfigurationName 'Default' } $SpeechConfiguration = $script:SpeechConfigurations.Default $Voices = $SpeechConfiguration.GetInstalledVoices() foreach ($voice in $Voices) { $CustomOutputObject = [pscustomobject]@{ Name = $voice.VoiceInfo.Name Age = $voice.VoiceInfo.Age Gender = $voice.VoiceInfo.Gender Culture = $voice.VoiceInfo.Culture Id = $voice.VoiceInfo.Id Description = $voice.VoiceInfo.Description SupportedAudioFormats = $voice.VoiceInfo.SupportedAudioFormats AdditionalInfo = $voice.VoiceInfo.AdditionalInfo Enabled = $voice.Enabled } $CustomOutputObject | Where-Object -FilterScript { ($null -eq $VoiceId -or $_.Id -in $VoiceId) -and ($null -eq $Name -or $_.Name -in $Name -or $_.Name -like $Name) -and ($null -eq $Age -or $_.Age -in $Age) -and ($null -eq $Gender -or $_.Gender -in $Gender) -and ($null -eq $Culture -or $_.Culture -in $Culture -or $_.Culture -like $Culture) } } } function Initialize-Speech { <# .SYNOPSIS Creates a System.Speech.Synthesis.SpeechSynthesizer object with the specified values .DESCRIPTION Creates a System.Speech.Synthesis.SpeechSynthesizer object and configures the specified values for Rate, Volume, and Voice .EXAMPLE PS C:\Initialize-Speech -Rate 3 -Volume 50 -Voice 'Microsoft David Desktop' -ConfigurationName 'DavidR3V50' .PARAMETER Rate Specifies the speech rate with higher values being faster. Valid range is -10 through 10. Default is 0. .PARAMETER Volume Specifies the speech volume with higher values being louder. Valid range is 1 through 100. Default is 100. .PARAMETER Voice Specifies the speech voice to user. Run Get-SpeechVoice to see valid values - use the name attribute. Default depends on the system language/culture settings. .PARAMETER ConfigurationName Specifies the ConfigurationName to use. Default is NULL. #> [cmdletbinding()] param( $Rate , $Volume , $Voice , $ConfigurationName ) Write-Verbose "Loading System.speech assembly" Add-Type -AssemblyName System.speech -ErrorAction Stop Write-Verbose "Creating Speech object" $SpeechConfiguration = New-Object System.Speech.Synthesis.SpeechSynthesizer -ErrorAction Stop foreach ($param in $PSBoundParameters.Keys) { if ($param -in 'Rate', 'Volume') { $SpeechConfiguration.$param = $($PSBoundParameters.$param) } if ($param -in 'Voice') { $SpeechConfiguration.SelectVoice($($PSBoundParameters.$param)) } } $Script:SpeechConfigurations.$ConfigurationName = $SpeechConfiguration } Function Out-Speech { <# .SYNOPSIS Accepts string or object input (non-string objects will be converted to strings using out-string) and outputs it to speech. .DESCRIPTION Accepts string or object input (non-string objects will be converted to strings using out-string) and outputs it to speech using the specified SpeechConfiguration and/or Rate, Volume, and Voice. The default output method is asynchronous (audio outputs while PowerShell continues). .PARAMETER InputObject Strings or objects that will be converted to speech and sent to the default audio device. .PARAMETER Rate Specifies the speech rate with higher values being faster. Valid range is -10 through 10. Default is 0. .PARAMETER Volume Specifies the speech volume with higher values being louder. Valid range is 1 through 100. Default is 100. .PARAMETER Voice Specifies the speech voice to user. Run Get-SpeechVoice to see valid values - use the name attribute. Default depends on the system language/culture settings. .PARAMETER ConfigurationName Specifies the ConfigurationName to use. Default is 'Default'. .PARAMETER SynchronousOutput Makes the audio output Synchronous (PowerShell pauses until the audio output has completed). .EXAMPLE "This is a test" | Out-Speech Description ----------- Speaks the string that was given to the function in the pipeline. .EXAMPLE Enable-SpeechConfiguration -ConfigurationName 'TooSlowTooSoft' -rate -2 -volume 5 "Today's date is $((get-date).toshortdatestring())" | Out-Speech -ConfigurationName 'TooSlowTooSoft' Description ----------- Sends 'Today's date is ______' to the default audio device. .EXAMPLE Enable-SpeechConfiguration -ConfigurationName 'TooSlowTooSoft' -rate -2 -volume 5 "Today's date is $((get-date).toshortdatestring())" | Out-Speech -ConfigurationName 'TooSlowTooSoft' -SynchronousOutput Description ----------- Sends 'Today's date is ______' to the default audio device and PowerShell waits for the output to complete. #> [cmdletbinding(DefaultParameterSetName = 'ASync')] Param ( [parameter(ParameterSetName = 'ASync', ValueFromPipeline = $true, Position = 1)] [parameter(ParameterSetName = 'Sync', ValueFromPipeline = $true, Position = 1)] [string[]]$InputObject , [parameter(ParameterSetName = 'ASync')] [parameter(ParameterSetName = 'Sync')] $ConfigurationName = 'Default' , [parameter(ParameterSetName = 'ASync')] [parameter(ParameterSetName = 'Sync')] [ValidateRange(-10, 10)] [Int]$Rate , [parameter(ParameterSetName = 'ASync')] [parameter(ParameterSetName = 'Sync')] [ValidateRange(1, 100)] [int]$Volume , [parameter(ParameterSetName = 'ASync')] [parameter(ParameterSetName = 'Sync')] [string]$voice , [parameter(ParameterSetName = 'Sync')] [switch]$SynchronousOutput ) Begin { $SpeechParams = @{ ErrorAction = 'Stop' ConfigurationName = $ConfigurationName } foreach ($param in $PSBoundParameters.Keys) { if ($param -in 'Rate', 'Volume', 'Voice', 'Verbose') { $SpeechParams.$param = $($PSBoundParameters.$param) } } switch ($script:SpeechConfigurations.ContainsKey($ConfigurationName)) { $true { #set Set-SpeechConfiguration @SpeechParams } $false { #enable Enable-SpeechConfiguration @SpeechParams } } $SpeechConfiguration = $script:SpeechConfigurations.$ConfigurationName } Process { ForEach ($line in $inputobject) { Write-Verbose "Speaking: $line" if ($PSCmdlet.ParameterSetName -eq 'Sync') { $null = $SpeechConfiguration.Speak(($line | Out-String)) } else { $null = $SpeechConfiguration.SpeakAsync(($line | Out-String)) } } } } ############################################################################################### # Import User's Configuration ############################################################################################### #Import-OutSpeechConfiguration $script:SpeechConfigurations = @{ } ############################################################################################### # Setup Tab Completion ############################################################################################### # Tab Completions for OutSpeech if ($null -ne $PSVersionTable -and $PSVersionTable.PSVersion.Major -ge 5) { Register-ArgumentCompleter -CommandName 'Enable-SpeechConfiguration', 'Get-SpeechVoice', 'Export-Speech', 'Out-Speech', 'Set-SpeechConfiguration', 'Get-SpeechConfiguration' -ParameterName Voice -ScriptBlock { param($CommandName, $ParameterName, $WordToComplete, $CommandAST, $FakeBoundParameter) $choices = @(Get-SpeechVoice).Name.where( { $_ -like "*$WordToComplete*" }) ForEach ($c in $choices) { [System.Management.Automation.CompletionResult]::new("'$c'", "'$c'", 'ParameterValue', "'$c'") } } Register-ArgumentCompleter -CommandName 'Enable-SpeechConfiguration', 'Get-SpeechVoice', 'Export-Speech', 'Out-Speech', 'Set-SpeechConfiguration', 'Get-SpeechConfiguration' -ParameterName VoiceId -ScriptBlock { param($CommandName, $ParameterName, $WordToComplete, $CommandAST, $FakeBoundParameter) $choices = @(Get-SpeechVoice).Id.where( { $_ -like "*$WordToComplete*" }) ForEach ($c in $choices) { [System.Management.Automation.CompletionResult]::new($c, $c, 'ParameterValue', $c) } } Register-ArgumentCompleter -CommandName 'Enable-SpeechConfiguration', 'Get-SpeechVoice', 'Export-Speech', 'Out-Speech', 'Set-SpeechConfiguration', 'Disable-SpeechConfiguration', 'Get-SpeechConfiguration' -ParameterName ConfigurationName -ScriptBlock { param($CommandName, $ParameterName, $WordToComplete, $CommandAST, $FakeBoundParameter) $choices = @($script:SpeechConfigurations.keys.where( { $_ -like "$WordToComplete*" })) ForEach ($c in $choices) { [System.Management.Automation.CompletionResult]::new($c, $c, 'ParameterValue', $c) } } } |