Tomorrow is Christmas, so this post is a day early, but I wanted to give you something you can play with when you’re done eating, drinking and getting merry. That’s the reason I won’t be giving you PowerShell or PowerCLI scripts that have anything to do with work.

You can do a lot more with PowerShell than you might think. Anything you can imagine you can create with PowerShell. It doesn’t matter if it is playing some music, displaying ASCII art, or something completely far-stretching mind boggling.

We wish you a Merry Christmas

We here at VMGuru want to wish you a Merry Christmas. Of course we want to do that in a nerd way. Last year Øyvind Kallstad posted a script that plays the song “We wish you a merry Christmas” from the command line. This is really something more than that you probably normally do with PowerShell.

The sound of it brings memories. Oh, those memories of custom build printer port addons like the Covox Speech plug that play MOD music through your printerport.

Don’t expect high quality audio from this script, but still: Enjoy the music.

try {
Add-Type -TypeDefinition @"
    public enum Duration
    {
        WHOLE     = 1600,
        HALF      = WHOLE/2,
        QUARTER   = HALF/2,
        EIGHTH    = QUARTER/2,
        SIXTEENTH = EIGHTH/2,
    }
"@
}
catch{ }

function ConvertTo-NoteFrequency {
    <#
        .SYNOPSIS
            Convert note names to MIDI note number and frequency.
        .DESCRIPTION
            This function takes a note name and will convert it to it's MIDI note number
            as well as the frequency of the note. It uses the 12-tone scale and
            supports both the 'regular', and the 'German' scale where 'H' is used instead of 'B'.
        .EXAMPLE
            ConvertTo-NoteFrequency 'A#4'
        .EXAMPLE
            ConvertTo-NoteFrequency 'b5' -UseAlternateScale
        .EXAMPLE
            (ConvertTo-NoteFrequency 'e5').Play(200)
        .EXAMPLE
            (ConvertTo-NoteFrequency 'e5').Play([Duration]::QUARTER)
        .NOTES
            Author: Øyvind Kallstad
            Date: 14.12.2014
            Version: 1.0
    #>
    [CmdletBinding()]
    param (
        # The name of the note.
        [Parameter(Position = 0, Mandatory = $true)]
        [ValidateNotNullorEmpty()]
        [string] $Note,

        # Default octave to append to note if none are present.
        [Parameter()]
        [ValidateRange(0,8)]
        [int] $DefaultOctave = 0,

        # Use this switch to use the alternate scale used in parts of northern europe where B = H.
        [Parameter()]
        [switch] $UseAlternateScale
    )

    # split note input into the note part and the octave part
    $noteSplit = $Note -split '(\d)'
    $noteName = $noteSplit[0]
    [int]$octave = $noteSplit[1]
    Write-Verbose "Note Name: $noteName"

    # convert to lower case
    $noteName = $noteName.ToLower()

    # replace flats with sharps
    $noteName = $noteName -replace 'db', 'c#'
    $noteName = $noteName -replace 'eb', 'd#'
    $noteName = $noteName -replace 'gb', 'f#'

    # handle alternate scale
    if($UseAlternateScale) {
        $scale  = @('a','a#','h','c','c#','d','d#','e','f','f#','g','g#')
        $noteName = $noteName -replace 'b', 'a#'
        $regexString = '^[acdefgh]#{0,1}$'
    }
    else {
        $scale  = @('a','a#','b','c','c#','d','d#','e','f','f#','g','g#')
        $noteName = $noteName -replace 'bb', 'a#'
        $regexString = '^[abcdefg]#{0,1}$'
    }

    Write-Verbose "Note Name after conversion: $noteName"

    # validate note name
    if (-not($noteName -match $regexString)) {
        Write-Warning "'$($Note)' is not a valid note name!"
    }

    else {
        # if no octave information is given, add default octave
        if(-not($octave)) {
            $octave = $DefaultOctave
        }
        Write-Verbose "Ocatave: $octave"

        # if note is above 'c', subtract 1 to the octave - since 'c' marks the beginning of the next octave
        $inputOctave = $octave
        if(-not($scale[0..2] -contains $noteName)) {
            if($octave -gt 0) {
                $octave--
                Write-Verbose "Octave after conversion: $($octave)"
            }
        }

        # find the number of half-steps up from 'A'
        $halfSteps = 0
        foreach ($scaleNote in $scale) {
            if(-not($scaleNote -eq $noteName)) {
                $halfSteps++
            }
            else {
                break
            }
        }
        Write-Verbose "Number of half-steps up from A: $($halfSteps)"

        # we initially set the frequency of A0
        # then update it with the frequency of 'A' in the same octave as the Note
        $aFrequencyInNoteOctave = 27.5
        for ($i = 0; $i -lt $octave; $i++) {
            $aFrequencyInNoteOctave = $aFrequencyInNoteOctave * 2
        }
        Write-Verbose "The frequency of 'A' in octave $($octave) is $($aFrequencyInNoteOctave)"

        # calculate the note frequency
        $noteFrequency = [math]::Pow([math]::Pow(2,(1/12)),$halfSteps) * $aFrequencyInNoteOctave

        # calculate the MIDI note number of the note
        $midiNoteNumber = [math]::Round(12 * [math]::Log(($noteFrequency/440),2)) + 69

        $output = New-Object -TypeName 'PSObject'
        $output | Add-Member -MemberType NoteProperty -Name 'Input' -Value $Note
        $output | Add-Member -MemberType NoteProperty -Name 'Note' -Value ($notaName + $inputOctave)
        $output | Add-Member -MemberType NoteProperty -Name 'Frequency' -Value $noteFrequency
        $output | Add-Member -MemberType NoteProperty -Name 'MidiNumber' -Value $midiNoteNumber
        $output | Add-Member -Name 'Play' -MemberType ScriptMethod -Value {param([int]$Length)[System.Console]::Beep($this.Frequency,$Length)}
        
        Write-Output $output
    }
}

# define melody
$melody = @(
    @('g4',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('c5',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('c5',[Duration]::EIGHTH),
    @('d5',[Duration]::EIGHTH),
    @('c5',[Duration]::EIGHTH),
    @('b4',[Duration]::EIGHTH),
    @('a4',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('a4',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('a4',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('d5',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('d5',[Duration]::EIGHTH),
    @('e5',[Duration]::EIGHTH),
    @('d5',[Duration]::EIGHTH),
    @('c5',[Duration]::EIGHTH),
    @('b4',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('g4',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH)
    @('g4',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('e5',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('e5',[Duration]::EIGHTH),
    @('f5',[Duration]::EIGHTH),
    @('e5',[Duration]::EIGHTH),
    @('d5',[Duration]::EIGHTH),
    @('c5',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('a4',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('g4',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('a4',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('d5',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('b4',[Duration]::EIGHTH), @('PAUSE',[Duration]::SIXTEENTH),
    @('c5',[Duration]::EIGHTH)
)

# play melody
foreach ($note in $melody) {
    if (-not($note[0] -eq 'PAUSE')) {
        (ConvertTo-NoteFrequency $note[0]).Play($note[1])
    }
    else {
        Start-Sleep -Milliseconds $note[1]
    }
}

I ‘wrote’ another Christmas script that shows you ASCII art. It’s not very advanced and flashy, but still.

$christmas = @"
     __,_,_,___)          _______
    (--| | |             (--/    ),_)        ,_) 
       | | |  _ ,_,_        |     |_ ,_ ' , _|_,_,_, _  ,
     __| | | (/_| | (_|     |     | ||  |/_)_| | | |(_|/_)___,
    (      |___,   ,__|     \____)  |__,           |__,

                            |                         _...._
                         \  _  /                    .::o:::::.
                          (\o/)                    .:::'''':o:.
                      ---  / \  ---                :o:_    _:::
                           >*<                     `:}_>()<_{:'
                          >0<@<                 @    `'//\\'`    @ 
                         >>>@<<*              @ #     //  \\     # @
                        >@>*<0<<<           __#_#____/'____'\____#_#__
                       >*>>@<<<@<<         [__________________________]
                      >@>>0<<<*<<@<         |=_- .-/\ /\ /\ /\--. =_-|
                     >*>>0<<@<<<@<<<        |-_= | \ \\ \\ \\ \ |-_=-|
                    >@>>*<<@<>*<<0<*<       |_=-=| / // // // / |_=-_|
      \*/          >0>>*<<@<>0><<*<@<<      |=_- |`-'`-'`-'`-'  |=_=-|
  ___\\U//___     >*>>@><0<<*>>@><*<0<<     | =_-| o          o |_==_| 
  |\\ | | \\|    >@>>0<*<<0>>@<<0<<<*<@<    |=_- | !     (    ! |=-_=|
  | \\| | _(UU)_ >((*))_>0><*<0><@<<<0<*<  _|-,-=| !    ).    ! |-_-=|_
  |\ \| || / //||.*.*.*.|>>@<<*<<@>><0<<@</=-((=_| ! __(:')__ ! |=_==_-\
  |\\_|_|&&_// ||*.*.*.*|_\\db//__     (\_/)-=))-|/^\=^=^^=^=/^\| _=-_-_\
  """"|'.'.'.|~~|.*.*.*|     ____|_   =('.')=//   ,------------.      
  jgs |'.'.'.|   ^^^^^^|____|>>>>>>|  ( ~~~ )/   (((((((())))))))   
      ~~~~~~~~         '""""`------'  `w---w`     `------------'
"@

Clear-Host

$christmas

I couldn’t find the author of the artwork. If you recognize it as your work, please let me know so I can give you credit for it.

If you’ve done something amazing with PowerShell for the holidays, let us know. We’re eager to learn.

That leaves us nothing more than to wish you a merry christmas and a happy new year.