TerminalTunes.types.ps1xml

<?xml version="1.0" encoding="utf-16"?>
<!-- Generated with EZOut 1.9.3: Install-Module EZOut or https://github.com/StartAutomating/EZOut -->
<Types>
  <Type>
    <Name>TerminalTunes.Generator</Name>
    <Members>
      <ScriptMethod>
        <Name>GetANSINoteRegex</Name>
        <Script>
                        [Regex]::new(@'
(?&lt;ANSI_Note&gt;
\e # An Escape
\[ # Followed by a bracket
(?&gt;
  (?&lt;Volume&gt;(?&lt;VolumeOff&gt;0) # 0 is no volume
    |
    (?&lt;VolumeLow&gt;[1-3]) # 1-3 is low volume
    |
    (?&lt;VolumeHigh&gt;[4-7]) # 4-7 is high volume
))\; # A semicolon separated the volume from the duration
# Duration is measured in 1/32 of a second
(?&lt;Duration&gt;\d+)\; # A semicolon separates the duration from the note
# One or more notes will follow
(?&lt;Notes&gt;(?:(?&gt;
  (?&lt;C5&gt;25) # 25 is C in the 6th octave (MIDI 96)
  |
  (?&lt;B5&gt;24) # 24 is B in the 5th octave (MIDI 95)
  |
  (?&lt;ASharp5&gt;23) # 23 is A Sharp in the 5th octave (MIDI 94)
  |
  (?&lt;A5&gt;22) # 22 is A in the 5th octave (MIDI 93)
  |
  (?&lt;GSharp5&gt;21) # 21 is G Sharp in the 5th octave (MIDI 92)
  |
  (?&lt;G5&gt;20) # 20 is G in the 5th octave (MIDI 91)
  |
  (?&lt;FSharp5&gt;19) # 19 is F Sharp in the 5th octave (MIDI 90)
  |
  (?&lt;F5&gt;18) # 18 is F in the 5th octave (MIDI 89)
  |
  (?&lt;E5&gt;17) # 17 is F in the 5th octave (MIDI 88)
  |
  (?&lt;DSharp5&gt;16) # 16 is D Sharp in the 5th octave (MIDI 87)
  |
  (?&lt;D5&gt;15) # 15 is D in the 5th octave (MIDI 86)
  |
  (?&lt;CSharp5&gt;14) # 14 is C Sharp in the 5th octave (MIDI 85)
  |
  (?&lt;C5&gt;13) # 13 is C in the 5th octave (MIDI 84)
  |
  (?&lt;B4&gt;12) # 12 is B in the 4th octave (MIDI 83)
  |
  (?&lt;ASharp4&gt;11) # 11 is A Sharp in the 4th octave (MIDI 82)
  |
  (?&lt;A4&gt;10) # 10 is A in the 4th octave (MIDI 81)
  |
  (?&lt;GSharp4&gt;9) # 9 is G Sharp in the 4th octave (MIDI 80)
  |
  (?&lt;G4&gt;8) # 8 is G in the 4th octave (MIDI 79)
  |
  (?&lt;FSharp4&gt;7) # 7 is F Sharp in the 4th octave (MIDI 78)
  |
  (?&lt;F4&gt;6) # 6 is F in the 4th octave (MIDI 77)
  |
  (?&lt;E4&gt;5) # 5 is E in the 4th octave (MIDI 76)
  |
  (?&lt;DSharp4&gt;4) # 4 is D Sharp in the 4th octave (MIDI 75)
  |
  (?&lt;D4&gt;3) # 3 is D in the 4th octave (MIDI 74)
  |
  (?&lt;CSharp4&gt;2) # 2 is C Sharp in the 4th octave (MIDI 73)
  |
  (?&lt;C4&gt;1) # 1 is C in the 4th octave (MIDI 72)
)\;?){0,}),~ # A command and a tilda end the sequence
)
'@, 'IgnoreCase,IgnorePatternWhitespace')

                    </Script>
      </ScriptMethod>
      <ScriptMethod>
        <Name>IsSafe</Name>
        <Script>
                        [ValidateScript({
    if ($_ -isnot [ScriptBlock]) { return $true }
    $astConditions = {
param($ast)
if ($ast -is [Management.Automation.Language.CommandAst]) {
    throw "AST cannot contain commands"
}
return $true} , {param($ast)
$included = [Int32],[Math],[TimeSpan],[DateTime],[String],[String[]],[Int32[]],[Double[]],[Management.Automation.SwitchParameter]
$excluded = '*'
if ($ast -is [Management.Automation.Language.TypeExpressionAst] -or
    $ast -is [Management.Automation.Language.TypeConstraintAst]) {
    $astType = $ast.TypeName
    $reflectionType = if ($astType) {
        $astType.GetReflectionType()
    }
    
    foreach ($inc in $included) {
        if ($inc -is [string] -and $astType -like $inc) {
            return $true
        }
        elseif ($inc -is [Regex] -and $astType -match $inc) {
            return $true
        }
        elseif ($inc -is [type]){
            if ($inc -eq $reflectionType) { return $true}
            if ($inc.IsSubclassOf($reflectionType) -or $reflectionType.IsSubclassOf($inc)) {
                return $true
            }
            if ($inc.IsInterface -and $reflectionType.getInterFace($inc)) {
                return $true
            }
            if ($reflectionType.IsInterface -and $inc.getInterFace($reflectionType)) {
                return $true
            }
        }
    }
    $throwMessage = "[$($ast.Typename)] is not allowed"
    foreach ($exc in $excluded) {
        if ($exc -is [string] -and $astType -like $exc) {
            throw $throwMessage
        }
        elseif ($exc -is [regex] -and $astType -match $exc) {
            throw $throwMessage
        }
        elseif ($exc -is [type]) {
            if ($ecx -eq $reflectionType) {
                throw $throwMessage
            }
            elseif ($exc.IsSubclassOf($reflectionType) -or $reflectionType.IsSubclassOf($exc)) {
                throw $throwMessage
            }
            elseif ($exc.IsInterface -and $reflectionType.getInterFace($exc)) {
                throw $throwMessage
            }
            elseif ($reflectionType.IsInterface -and $exc.getInterFace($reflectionType)) {
                throw $throwMessage
            }
        }
    }
}
return $true} , {
param($ast)
if ($ast -is [Management.Automation.Language.LoopStatementAst] -and
    $ast.GetType().Name -match '(?&gt;do|while)') {
    throw "ScriptBlock cannot contain $($ast.GetType().Name)"
}
return $true
}
    $scriptBlockAst = $_.Ast
    foreach ($astCondition in $astConditions) {
        $foundResults = $scriptBlockAst.FindAll($astCondition, $true)
        if (-not $foundResults) { return $false}
    }
    return $true
})]$ScriptBlockIsSafe = $this.ScriptBlock



&lt;#

$this.ScriptBlock.Ast.FindAll({
    param($ast)
    
    if ($ast -is [Management.Automation.Language.CommandAst]) {
        throw "AST cannot contain commands"
    }

    if ($ast -is [Management.Automation.Language.LoopStatementAst]) {
        if ($ast.GetType().Name -in 'DoWhileStatementAst','WhileStatementAst', 'DoUntilStatement') {
            throw "Cannot use $($ast.GetType().Name)"
        }
    }
    
    if ($ast -is [Management.Automation.Language.TypeExpressionAst]) {
        $reflectedType = $ast.TypeName.GetReflectionType()
        if (-not $reflectedType) {
            throw "Could not resolve $($ast.Typename.FullName)"
        }
        if ($reflectedType -notin [Math], [Timespan]) {
            throw "Unacceptable type"
        }
    }
    
}, $true)
#&gt;
return $true

                    </Script>
      </ScriptMethod>
      <ScriptMethod>
        <Name>IsSafe.ps</Name>
        <Script>
                        [ValidateScriptBlock(
    ExcludeCommand='*',
    NoWhileLoop,
    IncludeType={
        [int],[math],[timespan], [datetime],
        [string], [string[]],[int[]], [double[]],
        [switch]
    }
)]
$ScriptBlockIsSafe = $this.ScriptBlock



&lt;#

$this.ScriptBlock.Ast.FindAll({
    param($ast)
    
    if ($ast -is [Management.Automation.Language.CommandAst]) {
        throw "AST cannot contain commands"
    }

    if ($ast -is [Management.Automation.Language.LoopStatementAst]) {
        if ($ast.GetType().Name -in 'DoWhileStatementAst','WhileStatementAst', 'DoUntilStatement') {
            throw "Cannot use $($ast.GetType().Name)"
        }
    }
    
    if ($ast -is [Management.Automation.Language.TypeExpressionAst]) {
        $reflectedType = $ast.TypeName.GetReflectionType()
        if (-not $reflectedType) {
            throw "Could not resolve $($ast.Typename.FullName)"
        }
        if ($reflectedType -notin [Math], [Timespan]) {
            throw "Unacceptable type"
        }
    }
    
}, $true)
#&gt;
return $true
                    </Script>
      </ScriptMethod>
      <ScriptProperty>
        <Name>Title</Name>
        <GetScriptBlock>
                        $this.Name -replace '\.tune\.ps1$' -replace '[-_]', ' '
                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Tune</Name>
        <GetScriptBlock>
                        if (-not $this.IsSafe()) { return }
if ($this.LastParameters) {
    $lp = $this.LastParameters
    &amp; $this @lp
} else {
    &amp; $this
}
                    </GetScriptBlock>
      </ScriptProperty>
    </Members>
  </Type>
  <Type>
    <Name>TerminalTunes.Tune</Name>
    <Members>
      <ScriptMethod>
        <Name>GetANSINoteRegex</Name>
        <Script>
                        [Regex]::new(@'
(?&lt;ANSI_Note&gt;
\e # An Escape
\[ # Followed by a bracket
(?&gt;
  (?&lt;Volume&gt;(?&lt;VolumeOff&gt;0) # 0 is no volume
    |
    (?&lt;VolumeLow&gt;[1-3]) # 1-3 is low volume
    |
    (?&lt;VolumeHigh&gt;[4-7]) # 4-7 is high volume
))\; # A semicolon separated the volume from the duration
# Duration is measured in 1/32 of a second
(?&lt;Duration&gt;\d+)\; # A semicolon separates the duration from the note
# One or more notes will follow
(?&lt;Notes&gt;(?:(?&gt;
  (?&lt;C5&gt;25) # 25 is C in the 6th octave (MIDI 96)
  |
  (?&lt;B5&gt;24) # 24 is B in the 5th octave (MIDI 95)
  |
  (?&lt;ASharp5&gt;23) # 23 is A Sharp in the 5th octave (MIDI 94)
  |
  (?&lt;A5&gt;22) # 22 is A in the 5th octave (MIDI 93)
  |
  (?&lt;GSharp5&gt;21) # 21 is G Sharp in the 5th octave (MIDI 92)
  |
  (?&lt;G5&gt;20) # 20 is G in the 5th octave (MIDI 91)
  |
  (?&lt;FSharp5&gt;19) # 19 is F Sharp in the 5th octave (MIDI 90)
  |
  (?&lt;F5&gt;18) # 18 is F in the 5th octave (MIDI 89)
  |
  (?&lt;E5&gt;17) # 17 is F in the 5th octave (MIDI 88)
  |
  (?&lt;DSharp5&gt;16) # 16 is D Sharp in the 5th octave (MIDI 87)
  |
  (?&lt;D5&gt;15) # 15 is D in the 5th octave (MIDI 86)
  |
  (?&lt;CSharp5&gt;14) # 14 is C Sharp in the 5th octave (MIDI 85)
  |
  (?&lt;C5&gt;13) # 13 is C in the 5th octave (MIDI 84)
  |
  (?&lt;B4&gt;12) # 12 is B in the 4th octave (MIDI 83)
  |
  (?&lt;ASharp4&gt;11) # 11 is A Sharp in the 4th octave (MIDI 82)
  |
  (?&lt;A4&gt;10) # 10 is A in the 4th octave (MIDI 81)
  |
  (?&lt;GSharp4&gt;9) # 9 is G Sharp in the 4th octave (MIDI 80)
  |
  (?&lt;G4&gt;8) # 8 is G in the 4th octave (MIDI 79)
  |
  (?&lt;FSharp4&gt;7) # 7 is F Sharp in the 4th octave (MIDI 78)
  |
  (?&lt;F4&gt;6) # 6 is F in the 4th octave (MIDI 77)
  |
  (?&lt;E4&gt;5) # 5 is E in the 4th octave (MIDI 76)
  |
  (?&lt;DSharp4&gt;4) # 4 is D Sharp in the 4th octave (MIDI 75)
  |
  (?&lt;D4&gt;3) # 3 is D in the 4th octave (MIDI 74)
  |
  (?&lt;CSharp4&gt;2) # 2 is C Sharp in the 4th octave (MIDI 73)
  |
  (?&lt;C4&gt;1) # 1 is C in the 4th octave (MIDI 72)
)\;?){0,}),~ # A command and a tilda end the sequence
)
'@, 'IgnoreCase,IgnorePatternWhitespace')

                    </Script>
      </ScriptMethod>
      <ScriptMethod>
        <Name>GetNotes</Name>
        <Script>
                        $ansiNote = $this.GetANSINoteRegex()

if ($this.Tune) {
    foreach ($match in $ansiNote.Matches($this.Tune)) {
        $matchOut = [Ordered]@{}
        $noteStart = 0
        foreach ($group in $match.Groups) {
            if ($group.Name -eq 'Volume') {
                $matchOut[$group.Name] = $group.Value -as [int]
            }
            elseif ($group.Name -eq 'Duration') {
                $matchOut[$group.Name] =
                    [TimeSpan]::FromMilliseconds(($group.Value -as [int]) * (1/32) * 1000)
            }
            elseif ($group.Name -eq 'Notes') {
                $matchOut[$group.Name] = @()
                $noteStart = $group.Index
            }
            elseif ($noteStart -and $group.Success) {
                $matchOut.Notes += $group.Name -replace 'Sharp','#'
            }
        }
        $matchOut.Tune = $match.Value
        [PSCustomObject]$matchOut
    }
}
                    </Script>
      </ScriptMethod>
      <ScriptMethod>
        <Name>Play</Name>
        <Script>
                        param(
[Collections.IDictionary]
$Parameter
)

if ($this.IsGenerator) {
    $this | Add-Member LastParameters $Parameter -Force
}

if (-not $this.Tune) {
    return
}

[Console]::Write([Regex]::Replace($this.Tune, '[`\\]e', {"`e"}))
                    </Script>
      </ScriptMethod>
      <ScriptProperty>
        <Name>Duration</Name>
        <GetScriptBlock>
                        if (-not $this.CachedNoteCount) {
    $notes = @($this.GetNotes())
    [Timespan]$duration = 0
    $noteCount = 0
    foreach ($note in $notes) {
        $duration += ($note.Duration * ($note.Notes.Length))
        $noteCount += $note.Notes.Length
    }
    $this | Add-Member NoteProperty CachedDuration $duration -Force
    $this | Add-Member NoteProperty CachedNoteCount $noteCount -Force
}
if ($this.CachedDuration) {
    $this.CachedDuration
} else {
    $duration
}

                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>IsGenerator</Name>
        <GetScriptBlock>
                        $this -is [Management.Automation.ExternalScriptInfo]
                    </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>NoteCount</Name>
        <GetScriptBlock>
                        if (-not $this.CachedNoteCount) {
    $notes = @($this.GetNotes())
    [Timespan]$duration = 0
    $noteCount = 0
    foreach ($note in $notes) {
        $duration += ($note.Duration * ($note.Notes.Length))
        $noteCount += $note.Notes.Length
    }
    $this | Add-Member NoteProperty CachedDuration $duration -Force
    $this | Add-Member NoteProperty CachedNoteCount $noteCount -Force
}
if ($this.CachedNoteCount) {
    $this.CachedNoteCount
} else {
    $noteCount
}

                    </GetScriptBlock>
      </ScriptProperty>
    </Members>
  </Type>
</Types>