Archives for the Month of April, 2009

Things more likely to kill you than Swine Flu

There are a few. Based on worldwide numbers (160 confirmed deaths, 3000 “suspected” cases)

  • Falling out of bed (900 confirmed deaths)
  • Falling down the stairs (1,690 confirmed deaths)
  • Big storm (874 confirmed deaths)
  • Drinking binge (346 confirmed deaths)

Of course, the statistic left out by the news

  • Normal flu (36,000 confirmed deaths per year)

http://www.nsc.org/research/odds.aspx

PowerShell Audio Sequencer

I got forwarded an addictive interactive sequencer yesterday (http://lab.andre-michelle.com/tonematrix) and was immediately hooked. I asked an internal mailing list if there was any kind of hardware that lets you do this kind of thing on the couch, and got the response -- “you mean MIDI?” That’s close, but it is closer to a very simplified sequencer.

I play classical guitar... even being a fan of electronic music, I had never seen a sequencer used, or tried to make anything in one. I’m sure some researcher out there would love to have me for a “out of touch with reality” anthropology study.

Then I wondered, “Why should GUI folks have all the fun?”

88 lines later, a PowerShell Sequencer / Tracker was born: http://www.leeholmes.com/projects/PsTracker/PsTracker.zip. Even as a jaded scripter, I’m constantly amazed how compact PowerShell is. Given an example input:

# Replace any dash with something else to make a sound in that spot.
# Format: <NOTE><OCTAVE> <PATTERN>
# If you restrict yourself to a pentatonic scale (i.e. CDEGAC), anything sounds good.
# Instruments: # ([Toub.Sound.Midi.GeneralMidiInstruments] | gm -static -mem Property | % { $_.Name } ) -join " "

# .Instrument OverdrivenGuitar

C5 ---------OO-OO--
A5 -------OO-OO---O
G4 --------------O-
E4 ----------------
D4 X---X---X---X---
C4 ----------------
A4 ----------------
G3 ----X-----------
E3 ----------------
D3 ----------------
C3 ----------------
A3 -------OO-OO----
G2 ----------------
E2 ----------------
D2 ----------------
C2 ----------------

# .Instrument SquareLead
C6 -X-X-XX-X--XXX

# .Instrument Ocarina
C7 ---------------X-X-XX-X--XXX

# .Instrument Kalimba
C8 -----------------X---X---X---X--

This is all it takes to process it:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
#requires -Version 2
param($path, $bpm)

$scriptPath = & { Split-Path $myInvocation.ScriptName }

$trackEntries = @{}

function Update-Track
{
    $trackEntries.Clear()
    $instrument = $null

    foreach($line in Get-Content $path)
    {
        if($line -match ".*Instrument (.+)([\s]*)$")
        {
            $instrument = $matches[1]
            if(-not $trackEntries[$instrument]) { $trackEntries[$instrument] = @{} }
        }
        elseif($line -notmatch "#|(^[\s]*$)")
        {
            $note,$measures = -split $line
            for($measure = 0; $measure -lt $measures.Length; $measure++)
            {
                if($measures[$measure] -ne "-")
                {
                    $trackEntries[$instrument][$measure] = @($trackEntries[$instrument][$measure] + $note)
                }
                $trackEntries[$instrument]["Length"] = [Math]::Max($trackEntries[$instrument]["Length"], $measure)
            }
        }
    }
}

$fsw = New-Object System.IO.FileSystemWatcher (Split-Path (Resolve-Path $path).ProviderPath),$path
Register-ObjectEvent $fsw Changed -SourceIdentifier TrackUpdated

Update-Track

Add-Type -Path (Join-Path $scriptPath "Toub.Sound.Midi.dll")
[Toub.Sound.Midi.MidiPlayer]::OpenMidi()

try
{
    $sleep = 250
    if($bpm) { $sleep = 1000 * 120 / (8 * $bpm) }

    $currentMeasures = @{}
    while($true)
    {
        $activeNotes = @()
   
        foreach($instrument in $trackEntries.Keys)
        {
            if(-not $currentMeasures[$instrument]) { $currentMeasures[$instrument] = 0 }
            $mappedInstrument = [Toub.Sound.Midi.GeneralMidiInstruments]::$instrument

            [Toub.Sound.Midi.MidiPlayer]::Play(
                (New-Object Toub.Sound.Midi.ProgramChange 0,0,$mappedInstrument) )
   
            foreach($note in $trackEntries[$instrument][$currentMeasures[$instrument]])
            {
                [Toub.Sound.Midi.MidiPlayer]::Play( (New-Object Toub.Sound.Midi.NoteOn 0,0,$note,127) )
                $activeNotes += New-Object Toub.Sound.Midi.NoteOff 0,0,$note,127
            }

            $currentMeasures[$instrument] =
                ($currentMeasures[$instrument] + 1) % (1 + $trackEntries[$instrument]["Length"])
               
        }

        Start-Sleep -m $sleep
        $activeNotes | % { [Toub.Sound.Midi.MidiPlayer]::Play($_) }

        if(Get-Event *TrackUpdated*)
        {
            Remove-Event TrackUpdated

           
Update-Track
        }
    }
}
finally
{
    [Toub.Sound.Midi.MidiPlayer]::CloseMidi()
    Unregister-Event TrackUpdated
    Remove-Event *TrackUpdated*
}

 

For example:

.\Start-Tracker track.txt 60

If your system has a MIDI instrument for “Cowbells,” make sure to add more of them! This script builds on Stephen Toub's MIDI library, which I can't seem to find a reference to any longer.

As an aside, that research junket eventually led me to playing with a more feature-rich (free) sequencer called Linux Multimedia Studio. Keeping with the basis of starting with a pentatonic scale, this took only about an hour or two: http://www.leeholmes.com/projects/PsTracker/strive.mp3.