MIDIコントローラのnanoKONTROL2を買ったので、

入力データを取得して表示するPowerShellスクリプトを作りました。

 

 

動作画面

 

ソースコード


#----------------------------------------------------------------------------------------------------------------------
# Get-MidiData.ps1
#----------------------------------------------------------------------------------------------------------------------

Write-Output "* Get-MidiData"
Set-Location $PSScriptRoot

#----------------------------------------------------------------------------------------------------------------------
# MIDI Win32API Functions
#  https://docs.microsoft.com/en-us/windows/win32/api/mmeapi/

Add-Type @"
    using System;
    using System.Runtime.InteropServices;

    namespace Win32API {

        public enum ERR {
            MMSYSERR_NOERROR = 0,
            MMSYSERR_ERROR = 1,
            MMSYSERR_BADDEVICEID = 2,
            MMSYSERR_ALLOCATED = 4,
            MMSYSERR_INVALHANDLE = 5,
            MMSYSERR_NODRIVER = 6,
            MMSYSERR_NOMEM = 7,
            MMSYSERR_INVALFLAG = 10,
            MMSYSERR_INVALPARAM = 11,
        }

        public enum MSG {
            // MIDI Input
            MM_MIM_OPEN      = 0x3C1,
            MM_MIM_CLOSE     = 0x3C2,
            MM_MIM_DATA      = 0x3C3,
            MM_MIM_LONGDATA  = 0x3C4,
            MM_MIM_ERROR     = 0x3C5,
            MM_MIM_LONGERROR = 0x3C6,
            // MIDI Output
            MM_MOM_OPEN      = 0x3C7,
            MM_MOM_CLOSE     = 0x3C8,
            MM_MOM_DONE      = 0x3C9,
        }

        internal enum IE {
            MAXPNAMELEN = 32,
            CALLBACK_FUNCTION = 0x00030000,
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct MIDIINCAPS {                              // typedef struct {
            public UInt16 wMid;                                 //   WORD      wMid;
            public UInt16 wPid;                                 //   WORD      wPid;
            public UInt32 vDriverVersion;                       //   MMVERSION vDriverVersion;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)IE.MAXPNAMELEN)]
            public String szPname;                              //   TCHAR     szPname[MAXPNAMELEN];
            public UInt32 dwSupport;                            //   DWORD     dwSupport;
        }                                                       // } MIDIINCAPS;

        [StructLayout(LayoutKind.Sequential)]
        public struct MIDIOUTCAPS {                             // typedef struct {
            public UInt16 wMid;                                 //   WORD      wMid;
            public UInt16 wPid;                                 //   WORD      wPid;
            public UInt32 vDriverVersion;                       //   MMVERSION vDriverVersion;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)IE.MAXPNAMELEN)]
            public String szPname;                              //   TCHAR     szPname[MAXPNAMELEN];
            public UInt16 wTechnology;                          //   WORD      wTechnology;
            public UInt16 wVoices;                              //   WORD      wVoices;
            public UInt16 wNotes;                               //   WORD      wNotes;
            public UInt16 wChannelMask;                         //   WORD      wChannelMask;
            public UInt32 dwSupport;                            //   DWORD     dwSupport;
        }                                                       // } MIDIOUTCAPS;

    public class C {

        // MIDI Input Functions

        [DllImport("Winmm.dll")]
        public static extern UInt32 midiInGetNumDevs();         // UINT midiInGetNumDevs();

        public static UInt32 getSizeOfMIDIINCAPS() {
            return (UInt32)Marshal.SizeOf(typeof(MIDIINCAPS));
        }

        [DllImport("Winmm.dll")]
        public static extern UInt32 midiInGetDevCaps(           // MMRESULT midiInGetDevCaps(
            UInt32 uDeviceID,                                   //   UINT         uDeviceID,
            out MIDIINCAPS pmic,                                //   LPMIDIINCAPS pmic,
            UInt32 cbmic                                        //   UINT         cbmic
        );                                                      // );

        [DllImport("Winmm.dll")]
        public static extern UInt32 midiInOpen(                 // MMRESULT midiInOpen(
            out IntPtr phmi,                                    //   LPHMIDIIN phmi,
            UInt32 uDeviceID,                                   //   UINT      uDeviceID,
            MidiInProcDelegate dwCallback,                      //   DWORD_PTR dwCallback,
            IntPtr dwInstance,                                  //   DWORD_PTR dwInstance,
            UInt32 fdwOpen                                      //   DWORD     fdwOpen
        );                                                      // );

        public static UInt32 midiInOpen_Callback(
            out IntPtr phmi,                                    //   LPHMIDIIN phmi,
            UInt32 uDeviceID,                                   //   UINT      uDeviceID,
            IntPtr dwInstance                                   //   DWORD_PTR dwInstance,
        ) {
            return midiInOpen(out phmi, uDeviceID, MidiInProcRef, dwInstance, (UInt32)IE.CALLBACK_FUNCTION);
        }

        [DllImport("Winmm.dll")]
        public static extern UInt32 midiInClose(                // MMRESULT midiInClose(
            IntPtr hmi                                          //   HMIDIIN hmi
        );                                                      // );

        [DllImport("Winmm.dll")]
        public static extern UInt32 midiInReset(                // MMRESULT midiInReset(
            IntPtr hmi                                          //   HMIDIIN hmi
        );                                                      // );

        [DllImport("Winmm.dll")]
        public static extern UInt32 midiInStart(                // MMRESULT midiInStart(
            IntPtr hmi                                          //   HMIDIIN hmi
        );                                                      // );

        [DllImport("Winmm.dll")]
        public static extern UInt32 midiInStop(                 // MMRESULT midiInStop(
            IntPtr hmi                                          //   HMIDIIN hmi
        );                                                      // );

        public delegate void MidiInProcDelegate(                // void CALLBACK MidiInProc(
            IntPtr hmi,                                         //    HMIDIIN   hMidiIn,
            UInt32 wMsg,                                        //    UINT      wMsg,
            IntPtr dwInstance,                                  //    DWORD_PTR dwInstance,
            IntPtr dwParam1,                                    //    DWORD_PTR dwParam1,
            IntPtr dwParam2                                     //    DWORD_PTR dwParam2
        );                                                      // );

        public static event MidiInProcDelegate MidiInProcEvent;

        private static readonly MidiInProcDelegate MidiInProcRef = MidiInProc;

        private static void MidiInProc(                         // void CALLBACK MidiInProc(
            IntPtr hmi,                                         //    HMIDIIN   hMidiIn,
            UInt32 wMsg,                                        //    UINT      wMsg,
            IntPtr dwInstance,                                  //    DWORD_PTR dwInstance,
            IntPtr dwParam1,                                    //    DWORD_PTR dwParam1,
            IntPtr dwParam2                                     //    DWORD_PTR dwParam2
        ) {                                                     // );
            if (MidiInProcEvent != null) {
                MidiInProcEvent(hmi, wMsg, dwInstance, dwParam1, dwParam2);
            }
        }

        // MIDI Output Functions

        [DllImport("Winmm.dll")]
        public static extern UInt32 midiOutGetNumDevs();        // UINT midiOutGetNumDevs();

        public static UInt32 getSizeOfMIDIOUTCAPS() {
            return (UInt32)Marshal.SizeOf(typeof(MIDIOUTCAPS));
        }

        [DllImport("Winmm.dll")]
        public static extern UInt32 midiOutGetDevCaps(          // MMRESULT midiOutGetDevCaps(
            UInt32 uDeviceID,                                   //   UINT          uDeviceID,
            out MIDIOUTCAPS pmoc,                               //   LPMIDIOUTCAPS pmoc,
            UInt32 cbmoc                                        //   UINT          cbmoc
        );                                                      // );

    }
    }
"@

function Get-MidiInDevices() {
    $devs = New-Object System.Collections.ArrayList
    $num = [Win32API.C]::midiInGetNumDevs()
    for ($id = 0; $id -lt $num; $id++) {
        $caps = New-Object Win32API.MIDIINCAPS
        $ret = [Win32API.C]::midiInGetDevCaps($id, [ref]$caps, [Win32API.C]::getSizeOfMIDIINCAPS())
        if ($ret -eq [Win32API.ERR]::MMSYSERR_NOERROR) {
            [void]$devs.Add($caps)
        }
    }
    return $devs
}

function Get-MidiOutDevices() {
    $devs = New-Object System.Collections.ArrayList
    $num = [Win32API.C]::midiOutGetNumDevs()
    for ($id = 0; $id -lt $num; $id++) {
        $caps = New-Object Win32API.MIDIOUTCAPS
        $ret = [Win32API.C]::midiOutGetDevCaps($id, [ref]$caps, [Win32API.C]::getSizeOfMIDIOUTCAPS())
        if ($ret -eq [Win32API.ERR]::MMSYSERR_NOERROR) {
            [void]$devs.Add($caps)
        }
    }
    return $devs
}

#----------------------------------------------------------------------------------------------------------------------
# Main Routine

if ($null -eq $psISE) {
    $timeout = 60
    $ESC = @{
        CSI_SCOSC = "$([Char]27)[s";
        CSI_SCORC = "$([Char]27)[u";
        CSI_SGR_0 = "$([Char]27)[0m";
        CSI_SGR_7 = "$([Char]27)[7m"
    }
    $CSI_SCOSC_T = "$("`n" * 12)$([Char]27)[11A$([Char]27)[s"
} else {
    $timeout = 5
}

$midiInDevs = Get-MidiInDevices
Write-Host "* MIDI IN Device List:`n$($midiInDevs.szPname -join "`n")"

$midiOutDevs = Get-MidiOutDevices
Write-Host "* MIDI OUT Device List:`n$($midiOutDevs.szPname -join "`n")"

$midiInProcAction = {
    param ($hmi, $wMsg, $dwInstance, $dwParam1, $dwParam2)
    try {
        $ESC = $Event.MessageData.ESC

        switch ($wMsg) {
            ([UInt32][Win32API.MSG]::MM_MIM_DATA) {
                $name = ([Int32]$dwParam1 -band 0x0000ff00) -shr 8
                $value = ([Int32]$dwParam1 -band 0x00ff0000) -shr 16
                $Event.MessageData.status[$name] = $value
            }
        }
        $Event.MessageData.watch.Restart()

        Write-Host (("{0}{1}: dwInstance = {2}, dwParam1 = 0x{3:X8}, dwParam2 = 0x{4:X8}" -f
            $ESC.CSI_SCORC, [Enum]::Parse([Win32API.MSG], $wMsg), $dwInstance, [Int]$dwParam1, [Int]$dwParam2))
        $text = @"

KORG nanoKONTROL2
-- TRACK --                   P 10:** 11:** 12:** 13:** 14:** 15:** 16:** 17:**
<     >
3A:** 3B:** --- MARKER ---    S 20:** 21:** 22:** 23:** 24:** 25:** 26:** 27:**
CYCLE       SET   <     >     M 30:** 31:** 32:** 33:** 34:** 35:** 36:** 37:**
2E:**       3C:** 3D:** 3E:** R 40:** 41:** 42:** 43:** 44:** 45:** 46:** 47:**
<<    >>    []    >     O
2B:** 2C:** 2A:** 29:** 2D:** V 00:** 01:** 02:** 03:** 04:** 05:** 06:** 07:**

"@
        $Event.MessageData.status.GetEnumerator() |
        ForEach-Object {
            $CSI_SGR = @($ESC.CSI_SGR_0, $ESC.CSI_SGR_7)[$_.Value -ne 0]
            $text = $text -replace ("($($_.Name.ToString("X2")))\:\*\*"),
                "`$1:$CSI_SGR$($_.Value.ToString("X2"))$($ESC.CSI_SGR_0)"
        }
        Write-Host $text

    } catch [Exception] {
        Write-Warning ($_ | Out-String)
    }
}

$watch = New-Object System.Diagnostics.Stopwatch
$status = @{}
Write-Host "* Start Monitoring"
Write-Host "$CSI_SCOSC_T" -NoNewline
Get-Event | Remove-Event
$event = Register-ObjectEvent -InputObject ($obj = [Win32API.C]) -EventName MidiInProcEvent `
    -Action $midiInProcAction `
    -MessageData (New-Object PSObject -Property @{ status = $status; watch = $watch; ESC = $ESC })

$id = ([Collections.Generic.List[Object]]$midiInDevs).FindIndex({ $args.szPname -eq "nanoKONTROL2" })
if ($id -eq -1) {
    $id = 0
}
$hmi = [IntPtr]::Zero
$ret = [Win32API.C]::midiInOpen_Callback([ref]$hmi, $id, [IntPtr]::Zero)
if ($ret -ne [Win32API.ERR]::MMSYSERR_NOERROR) {
    Write-Host "! midiInOpen(uDeviceID = $id) failed: $([Enum]::Parse([Win32API.ERR], $ret))"
} else {
    $watch.Restart()
    $ret = [Win32API.C]::midiInStart($hmi)
    while ($true) {
        if ($Host.UI.RawUI.KeyAvailable) {
            $key = $Host.ui.RawUI.ReadKey("NoEcho,IncludeKeyUp")
            if ($key.VirtualKeyCode -eq 27) {
                break
            }
        }
        if ($watch.Elapsed.TotalSeconds -ge $timeout) {
            break
        }
    }
    $ret = [Win32API.C]::midiInStop($hmi)
    $ret = [Win32API.C]::midiInReset($hmi)
    $ret = [Win32API.C]::midiInClose($hmi)
}

Unregister-Event $event.Name
Get-Event | Remove-Event
Write-Host "* Stop Monitoring"

exit

1-Wire通信インターフェイスのデバイス検索アルゴリズム(AN-187)を見て、

その簡略版(8ビットアドレス版)のデモをPowerShellで作ってみました。

 

実行するとこのようになります。

> .\Search-DeviceAddress.ps1
* Search-DeviceAddress.ps1
* Prepared Device List
Device Address = E9h
Device Address = 69h
Device Address = EBh
* Search Device
bit=0, data0=1(111), data1=0(000), next=1, zero=-1
bit=1, data0=0(001), data1=0(110), next=0, zero=1
bit=2, data0=0(001), data1=1(111), next=0, zero=1
bit=3, data0=1(111), data1=0(001), next=1, zero=1
bit=4, data0=0(001), data1=1(111), next=0, zero=1
bit=5, data0=1(111), data1=0(001), next=1, zero=1
bit=6, data0=1(111), data1=0(001), next=1, zero=1
bit=7, data0=0(101), data1=0(011), next=0, zero=7
Device Address = 69h
* Search Device
bit=0, data0=1(111), data1=0(000), next=1, zero=-1
bit=1, data0=0(001), data1=0(110), next=0, zero=1
bit=2, data0=0(001), data1=1(111), next=0, zero=1
bit=3, data0=1(111), data1=0(001), next=1, zero=1
bit=4, data0=0(001), data1=1(111), next=0, zero=1
bit=5, data0=1(111), data1=0(001), next=1, zero=1
bit=6, data0=1(111), data1=0(001), next=1, zero=1
bit=7, data0=0(101), data1=0(011), next=1, zero=1
Device Address = E9h
* Search Device
bit=0, data0=1(111), data1=0(000), next=1, zero=-1
bit=1, data0=0(001), data1=0(110), next=1, zero=-1
bit=2, data0=0(110), data1=1(111), next=0, zero=-1
bit=3, data0=1(111), data1=0(110), next=1, zero=-1
bit=4, data0=0(110), data1=1(111), next=0, zero=-1
bit=5, data0=1(111), data1=0(110), next=1, zero=-1
bit=6, data0=1(111), data1=0(110), next=1, zero=-1
bit=7, data0=1(111), data1=0(110), next=1, zero=-1
Device Address = EBh
* Searched Device List
Device Address = 69h
Device Address = E9h
Device Address = EBh

 

ソースコード

#----------------------------------------------------------------------------------------------
# Search-DeviceAddress.ps1
#----------------------------------------------------------------------------------------------

Write-Output "* Search-DeviceAddress.ps1"
Set-Location (Split-Path $MyInvocation.MyCommand.path -Parent)

#----------------------------------------------------------------------------------------------
# Device Class

class Device
{
    [int]$Address
    [int]$Bit
    [bool]$Wait

    Device($Address) {
        $this.Address = $Address
        $this.Bit = 0
        $this.Wait = $false
    }

    [void] Reset() {
        $this.Bit = 0
        $this.Wait = $false
    }

    [int] Read([int]$Data) {
        if ($this.Wait) {
            return 1
        }
        return ((($this.Address -shr $this.Bit) -bxor $Data) -band 1)
    }

    [void] Write([int]$Data) {
        if ((($this.Address -shr $this.Bit) -bxor $Data) -band 1) {
            $this.Wait = $true
        }
        $this.Bit++
    }
}

#----------------------------------------------------------------------------------------------
# Search Device Function

function Search-Device() {
    $list = New-Object System.Collections.ArrayList
    $address = 0;
    $branch = -1

    do {
        Write-Host "* Search Device"
        $zero = -1
        $Devices.ForEach({ $_.Reset() })
        $result = $true

        for ($bit = 0; $bit -lt 8; $bit++) {
            $data0, $text0 = -1, ""
            $data1, $text1 = -1, ""
            $Devices.ForEach({
                $data = $_.Read(0);
                $data0 = $data0 -band $data
                $text0 += $data;
                $data = $_.Read(1);
                $data1 = $data1 -band $data
                $text1 += $data;
            })
            Write-Host "bit=$bit, data0=$data0($text0), data1=$data1($text1)" -NoNewline

            if ($data0 -band $data1) {
                $result = $false
                Write-Host ", No Device"
                break
            }
            if ($data0 -bxor $data1) {
                $next = $data0
            } else {
                if ($bit -lt $branch) {
                    $next = ($address -shr $bit) -band 1
                } elseif ($bit -eq $branch) {
                    $next = 1
                } else {
                    $next = 0
                }
                if ($next -eq 0) {
                    $zero = $bit
                }
            }
            Write-Host ", next=$next, zero=$zero"
            $address = ($address -band (-bnot (1 -shl $bit))) -bor ($next -shl $bit)
            $Devices.ForEach({ $_.Write($next) })
        }
        if ($result) {
            [void]$list.Add($address)
            $branch = $zero
            Write-Host ("Device Address = {0:X2}h" -f $address)
        }
    } while ($branch -ne -1)

    return $list
}

#----------------------------------------------------------------------------------------------
# Main Routine

Write-Host "* Prepared Device List"
$Devices = New-Object System.Collections.ArrayList
$n = Get-Random -Minimum 2 -Maximum 8
for ($i = 0; $i -lt $n; $i++) {
    $address = Get-Random -Minimum 0 -Maximum 256
    [void]$Devices.Add((New-Object Device($address)))
    Write-Host ("Device Address = {0:X2}h" -f $address)
}

$list = Search-Device

Write-Host "* Searched Device List"
$list | ForEach-Object {
    Write-Host ("Device Address = {0:X2}h" -f $_)
}

exit

ほとんど白めだかになってしまった最後のスーパーブラックめだか1匹がいなくなった…。

今残っているのは子メダカ2匹のみ。

 

約1ヶ月ぶりの更新になりました。

現在、親メダカは最初に買ったスーパーブラックめだかのうち

やや薄めの1匹のみとなりました。それで、ほとんど白っぽくなりました…。

子メダカは4匹になりました。

ミナミヌマエビは20匹ほどいます。

 

下は今の写真ですが、左のニューラージリーフ・ハイグロフィラは

相変わらず成長が著しく、根も見苦しい限りです。

 

 

ブログは更新できていなかったのですが、

写真は撮っていましたので載せたいと思います。

 

2018/06/15(Sat)

 

2018/06/23(Sat)

 

2018/06/24(Sat)

 

2018/07/08(Sun) 先週

 

最初に買ったスーパーブラックメダカ2匹のうち、

より黒くてもう1匹を追い掛け回す方だったメダカが、今朝亡くなりました。

 

数日前から異常にお腹が膨れて、

常に水面近くを泳いで口をパクパクしていたので心配していたのですが、

あっという間でした。

 

残念。

先週亡くなった幹之メダカはミナミヌマエビに食べられて2~3日で跡形もなくなりました。

最近、茶ゴケが増えてきたので、成長の早いマツモを入れました。

 

左のニューラージリーフ・ハイグロフィラは成長が著しく、

横に曲がった茎から根が生えてきましたので、

もうそろそろ剪定が必要かもしれません。

 

 

5/1(火)の朝、やせ細り病になっていた幹之メダカが亡くなってから1ヶ月。

今朝6/1(金)、もう一匹の幹之メダカも亡くなりました。

 

2~3日前から常に水面ギリギリを泳いでいて餌も食べなくなってきたので

心配していたのですが、あっという間の急死でした。残念。

 

追加した流木が沈むようになってレイアウトを考えていたのですが、

右奥のカーナミン?がどうしても目障り。

高さの低い水槽のため横に伸びて根が生えてしまいます。

そのため、茎の太い部分を残して植え替えました。

 

下は先週の写真です。写真では分かりませんが、

流木の裏にステンレスのネジを重りとしてつけて沈めています。

 

こちらは今朝の写真です。すっきりしました。

メダカの稚魚も全て稚魚ケースから親水槽に移しました。

メダカの稚魚は8匹くらい、

ミナミヌマエビの稚エビも20匹くらいになってしまったので、

もう増やさないようにします。

 

そういえば先々週に、「ハイグロフィラ・ピンナティフィダ」という水草を買って、

流木に活着させたのでした。

 

 

 

昨日は水槽の大掃除をしました。

 

(1) 2リットルペットボトルを横にして上部に穴を開け、ペットボトル水槽を作る。

(2) 水槽水を3分の1程度だけペットボトル水槽に移す。

(3) 水草や流木を取り出す。水草はビニール袋に入れて乾燥防止する。

(4) 雲山石を取り出して、乾かす。

(5) 生体(メダカやミナミヌマエビ)をコップですくって取り出し、ペットボトル水槽に移す。
(6) 稚エビは都度スポイトで取り出す。… たぶん20匹くらいは居た。

(7) 大磯砂を全てバケツに移し、カルキ抜きした水道水でゆすぎ洗いをする。

(8) 残りの水槽水をバケツに移し、水槽の中を空にする。

(9) フィルターをジェックス「サイレントフロー スリム ブラック」に交換する。

(10) 雲山石 → 大磯砂 → バケツに退避しておいた水槽水 → 水草を入れ、

   ペットボトル水槽の口から水槽水と生体をまるごと水槽に注いで掃除完了。

 

※ ホトニアは、どこかのページに「水質や光量等の条件が合わないと、

  急に溶けてしまう場合があるので注意」と書いてありましたが、

  その通り急に溶けてしまうので処分しました。

※ ロタラも、成長が早すぎて小型水槽には合わないので処分しました。
※ 雲山石はぐらぐらして不安定でしたので、ダイソーで買ったディスプレーケースの

  台座の上に乗せて、グルーガンで固定したものを水槽の中に入れました。

※ 元々使っていたコトブキ工芸「ミニボックスコーナー170」は活性炭の効果がイマイチで、

  ブラックホールという活性炭をフィルタの吸込口の前に置いていました。
  サイレントフロー スリムは内部の隙間にこれを入れられるので便利です。

  

下が今朝撮った写真です。

新しい流木も1個入れています。沈むようになったら配置したいと思います。

 

 

斜め上からの写真。

 

5/1(火)の朝、やせ細り病になっていた白メダカが亡くなりました。合掌。

 

 

そして今日。

水槽内で水草についていた卵を見つけたので、
稚魚ケースの中に入れたところ、一匹だけ他より大きい

全長1cm程度の稚魚が卵をつつくようになりました。

そのため、その一匹を親水槽に入れることにしました。

 

 

スーパーブラックメダカの雄と幹之(みゆき)メダカの雌との子供。

体は白色になりました。