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)
    }
  }
}