Summary: Use a hash table and an array to run prearranged actions.
Honorary Scripting Guy, Sean Kearney here. I’m filling in for our good friend, Ed again today. I think he’s busy playing some tunes with the Windows PowerShell piano we created last week.
So continuing from last weekend’s silliness, I had the song “Popcorn” in my head. I was thinking, “Is there any way we could have some prerecorded music for Windows PowerShell?”
Well, this is definitely something we can do, but we need to figure out a few details to play music. Looking at a typically sheet of music, we have some details to be aware of:
Note A note (A,B,C,D) could be sharp, flat, or neither (let’s say, neutral).
Octave Our note would be an octave, and there can be multiple C notes at different frequencies.
Time Timing could be quarter, half, or whole (even eighth or sixteenth, but we’ll start simple). The note could also be “dotted” (plays 50% longer than its variant).
Beats Each measure plays a certain number of beats at a time (4/4 is what we’ll work with).
So before Ludwig Von Beethoven comes out to correct my musical errors, we’re going to define a special array. We’re going to change our original music array from last week slightly.
First, instead of a key on the keyboard, we’re going to create a new pattern for musical notes. We’re going to identify octave, note, and sharp or flat with this simple designation:
# First Column Identifies Octave (A-C) (where C is the highest) - Octave 0 = REST
# Second Column Identifies Note
# Third Column Designates Note as Sharp "S" or Not "N"
For a C note in the second octave, it will look like this:
BCN
A D# in the third octave looks like this
CDS
And of course, a rest note will have a special designation of RRR. So our list of notes will translate to this:
[array]$notes=$NULL
$notes+=@{"RRR"=37}
$notes+=@{"BCN"=290}
$notes+=@{"BCS"=310}
$notes+=@{"BDN"=330}
$notes+=@{"BDS"=345}
$notes+=@{"BEN"=360}
$notes+=@{"BFN"=390}
$notes+=@{"BFS"=415}
$notes+=@{"BGN"=440}
$notes+=@{"BGS"=460}
$notes+=@{"CAN"=480}
$notes+=@{"CAS"=505}
$notes+=@{"CBN"=530}
$notes+=@{"CCN"=580}
$notes+=@{"CCS"=605}
$notes+=@{"CDN"=630}
$notes+=@{"CDS"=670}
$notes+=@{"CEN"=710}
$notes+=@{"CFN"=710}
$notes+=@{"CFN"=710}
$notes+=@{"CGN"=710}
$notes+=@{"CGN"=710}
After that, we need to define some particular details for notes and duration. For time, we’ll base everything on 4/4 and over two blocks of measures:
Eighth note 8
Quarter note 4
Half note 2
Whole note 1
To plug in a dotted note, we’ll add an additional column, 1 or 0 for dotted or non-dotted.
So to play an F#, second octave, whole note, non-dotted, we would use this:
BFS10
Or to play a G note, third octave, half note, dotted, we would use this:
CGN21
So for our data array, the first three entries will contain the note, and the last pair is how long we’ll play it. Let’s plug-in some data directly:
# Whole Note Dotted
$Length=1
$Dotted=1
$PlayTime=4*(1/$Length)*(.5*[int]($Dotted="1"))
Cool. So let’s put this together as a small array to play “Popcorn”:
$MusicString=$NULL
$MusicString+="CEN80CDN80CEN80CCN80CAN80CCN80BGN80RRR80"
$MusicString+="CEN80CDN80CEN80CCN80CAN80CCN80BGN80RRR80"
$MusicString+="CEN80CFS80CGN80CFS80CGN80CEN80CFS80CEN80"
$MusicString+="CFS80CDN80CEN80CCN80CAN80CCN80CEN80"
We’ll create a loop to parse the data, and pass the results through to the array to determine the note and duration:
$Start=0
$End=$musicstring.Length
do {
$Data=$MusicString.Substring($Start,5)
$Note=$data.substring(0,3)
$Length=[int]$data.substring(3,1)
$Dotted=$data.substring(4,1)
$Tone=$Notes.$Note
$PlayTime=4*(1/$length)*(.5*[int]($Dotted="1"))
With this data in hand, let’s play the music:
If ($Note -ne "RRR")
{[console]::beep($tone,300*($Playtime))}
Else
{START-SLEEP -milliseconds (600*$Playtime)}
$Start=$Start+5
} Until ($Start -ge $End)
TaDa! You’re a musician with Windows PowerShell!
If you’d like to download and listen to this silly little script, it’s sitting in the TechNet Gallery: Windows PowerShell Popcorn Tune.
You can adapt the music array to meet your own needs, including speeding it up and optimizing it (maybe even tune my horridly guessed notes).
Tune in tomorrow when we use Windows PowerShell to cook up a little popcorn for our script.
I invite you to follow the Scripting Guys on Twitter and Facebook. If you have any questions, send email to scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Sean Kearney,
Honorary Scripting Guy and Windows PowerShell MVP