超簡易 MIDI キーボード for cygwin Perl2006年12月07日 02時03分16秒

実行画面

概要

PC のキーボードを MIDI キーボードにしてしまうという、激しくありがちなプログラムです。MIDI マッパーが正しく設定されていればふつーに音が鳴ります。

操作方法

画面の表示に合わせてキーを押すと音が鳴り、放せば止まります。

また、カーソルキーの上下でオクターブを移動することができます。

動作環境

  • 本プログラムは cygwin 上の Perl にて動作確認していますが、Win32API::MIDI をビルドして動かすことができるようであれば、ActivePerl 上でも動かせるかもしれません。
  • Windows 以外の OS では動作しません。
  • Win32API::MIDI モジュールを追加インストールする必要があります。
  • Win32::Console のバージョンが古いと、定義済み変数であるはずの変数が定義されていない為に例外が発生するかもしれません。その場合は、最新のモジュールに差し替えてください。

制限事項

音色の差し替えやベロシティの変更など、その他のいろんなことは一切できません。

また、キーリピートが発生するぐらい押しっぱなしにしていると、何故か和音が 3 つぐらいまでしか鳴らせなくなっちゃったりします。

免責事項

Artistic License (参考: 日本語訳) に準じます。ご自由にどうぞ。また、一切の保証はありません。ご利用は自己責任で。

ソースコード

#!/usr/bin/perl

package Keybord;

use strict;

use Win32::Console;

# 鍵盤のキー割り当て
use constant    KEYBORD_MAP_WHITE_LOW       => [qw(z x c v b n m , . / \\)];
use constant    KEYBORD_MAP_BLACK_LOW       => [qw(s d), '', qw(g h j), '', qw(l ;)];
use constant    KEYBORD_MAP_WHITE_HIGH      => [qw(q w e r t y u i o p @ [)];
use constant    KEYBORD_MAP_BLACK_HIGH      => [qw(2 3 4), '', qw(6 7), '', qw(9 0 -)];

# 鍵盤の垂直表示位置
use constant    KEYBORD_POS_Y_WHITE_LOW     => [ 12, 20 ];
use constant    KEYBORD_POS_Y_BLACK_LOW     => [ 12, 16 ];
use constant    KEYBORD_POS_Y_WHITE_HIGH    => [  2, 10 ];
use constant    KEYBORD_POS_Y_BLACK_HIGH    => [  2,  6 ];

# 鍵盤の水平表示位置
use constant    KEYBORD_POS_X_LOW           => 10;
use constant    KEYBORD_POS_X_HIGH          => 8;

# 鍵盤の幅
use constant    KEY_WIDTH_WHITE             => 4;
use constant    KEY_WIDTH_BLACK             => 2;

# 鍵盤の色
use constant    KEY_COLOR_WHITE             => $FG_BLACK | $BG_LIGHTGRAY;
use constant    KEY_COLOR_BLACK             => $FG_LIGHTGRAY | $BG_BLACK;
use constant    KEY_COLOR_HIGHLIGHT         => $FG_BLACK | $BG_YELLOW;

# 鍵盤の最初のキーに割り当てられるノート値 (デフォルト値)
use constant    FIRST_NOTE_LOW              => 48;
use constant    FIRST_NOTE_HIGH             => 65;

# オクターブシフトで変化するノート値
use constant    OCTAVE                      => 12;

# オクターブシフトの閾値
use constant    NOTE_UNDER_LIMIT            => 0;
use constant    NOTE_UPPER_LIMIT            => 127 - scalar(@{&KEYBORD_MAP_WHITE_HIGH}) * 2;

# 鍵盤のキーに対する各種情報 (ノート値差分、インデクス、白鍵 or 黒鍵、押下中か否か)
my %keybord_info_low;
my %keybord_info_high;
{
    my $setNoteDiff = sub {
        my ($note_diff, $white, $black) = @_;
        for (my $i = 0, my $note = 0; $i < @$white; $i++){
            $note_diff->{$white->[$i]} = {
                'note' => $note++,
                'index' => $i,
                'type' => 'white',
                'is down' => 0,
            };
            $note_diff->{$black->[$i]} = {
                'note' => $note++,
                'index' => $i,
                'type' => 'black',
                'is down' => 0
            }                               if $black->[$i] ne '';
        }
    };
    $setNoteDiff->(\%keybord_info_low, KEYBORD_MAP_WHITE_LOW, KEYBORD_MAP_BLACK_LOW);
    $setNoteDiff->(\%keybord_info_high, KEYBORD_MAP_WHITE_HIGH, KEYBORD_MAP_BLACK_HIGH);
}

# コンソール出力オブジェクト
our $console_out = new Win32::Console(STD_OUTPUT_HANDLE);

# パッケージローカル関数
my ($displayKeybord);

# オブジェクト生成 - 画面作り
sub new {
    my $invocant = shift;
    my $class = ref $invocant || $invocant;
    
    $console_out->Cls($FG_BLACK | $BG_BLUE);
    $displayKeybord->(KEYBORD_MAP_WHITE_LOW, KEYBORD_POS_X_LOW, 'white', KEYBORD_POS_Y_WHITE_LOW);
    $displayKeybord->(KEYBORD_MAP_WHITE_HIGH, KEYBORD_POS_X_HIGH, 'white', KEYBORD_POS_Y_WHITE_HIGH);
    $displayKeybord->(KEYBORD_MAP_BLACK_LOW, KEYBORD_POS_X_LOW, 'black', KEYBORD_POS_Y_BLACK_LOW);
    $displayKeybord->(KEYBORD_MAP_BLACK_HIGH, KEYBORD_POS_X_HIGH, 'black', KEYBORD_POS_Y_BLACK_HIGH);
    
    my $self = {
        'first note low'    => FIRST_NOTE_LOW,      # 下鍵盤の最初のキーのノート値
        'first note high'   => FIRST_NOTE_HIGH,     # 上鍵盤の (以下略)
    };
    
    bless $self, $class
}

# デストラクタ - 画面消去
sub DESTROY {
    $console_out->Cls($ATTR_NORMAL);
}

# キー on/off 切り替え - 対応するノート値を返す
sub keyTurn {
    my ($self, $key, $is_down) = @_;
    my $high_or_low =     $keybord_info_low{$key}   ? 'low'
                        : $keybord_info_high{$key}  ? 'high'
                        :                             undef;
    return  unless $high_or_low;
    
    my $keybord = $high_or_low eq 'low' ? \%keybord_info_low : \%keybord_info_high;
    # リピート防止の為、押し続けている間はノート値を返さない
    return  if $keybord->{$key}{'is down'} && $is_down;
    
    $keybord->{$key}{'is down'} = $is_down;
    my ($note, $index, $type) = @{$keybord->{$key}}{qw(note index type)};
    my $y = $high_or_low eq 'low'   ? ($type eq 'white' ? KEYBORD_POS_Y_WHITE_LOW->[1]
                                                        : KEYBORD_POS_Y_BLACK_LOW->[1])
                                    : ($type eq 'white' ? KEYBORD_POS_Y_WHITE_HIGH->[1]
                                                        : KEYBORD_POS_Y_BLACK_HIGH->[1]);
    # ハイライトの切り替え
    $console_out->FillAttr(
        $is_down    ? KEY_COLOR_HIGHLIGHT
                    : $type eq 'white' ? KEY_COLOR_WHITE : KEY_COLOR_BLACK,
        $type eq 'white' ? KEY_WIDTH_WHITE : KEY_WIDTH_BLACK,
        ($high_or_low eq 'low' ? KEYBORD_POS_X_LOW : KEYBORD_POS_X_HIGH) +
            ($type eq 'white' ? 0 : KEY_WIDTH_WHITE - int(KEY_WIDTH_BLACK / 2)) +
            KEY_WIDTH_WHITE * $index,
        $y);
    
    $note + $self->{"first note $high_or_low"}
}

# オクターブを上げる / 下げる
sub moveOctave {
    my ($self, $updown) = @_;
    return  if $updown eq 'down' && $self->{'first note low'} - OCTAVE < NOTE_UNDER_LIMIT;
    return  if $updown eq 'up' && $self->{'first note high'} + OCTAVE > NOTE_UPPER_LIMIT;
    
    $self->{'first note low'} += $updown eq 'up' ? OCTAVE : - OCTAVE;
    $self->{'first note high'} += $updown eq 'up' ? OCTAVE : - OCTAVE;
}

# 画面作り - キーボード描画
$displayKeybord = sub {
    my ($keymap, $x, $type) = (shift, shift, shift);
    my ($y1, $y2) = @{shift()};
    $x += $type eq 'white' ? 0 : KEY_WIDTH_WHITE - int(KEY_WIDTH_BLACK / 2);
    local $_;
    for my $key (@$keymap){
        next    if $key eq '';
        $console_out->FillAttr(
            $type eq 'white'    ? (KEY_COLOR_WHITE, KEY_WIDTH_WHITE, $x, $_)
                                : (KEY_COLOR_BLACK, KEY_WIDTH_BLACK, $x, $_)
            )   for ($y1 .. $y2);
        $console_out->WriteChar($key, $x, $y2);
    }
    continue {
        $x += KEY_WIDTH_WHITE;
    }
};

package main;

use strict;

use Win32::Console;
use Win32API::MIDI;

# 仮想キー
use constant    VKEY_END                => 27;  # 終了キー : [ESC]
use constant    VKEY_OCTAVE_UP          => 38;  # オクターブを上げる : カーソルキー [↑]
use constant    VKEY_OCTAVE_DOWN        => 40;  # オクターブを下げる : カーソルキー [↓]


# MIDI イベント
use constant    MIDI_STATUS_NOTE_ON     => 0x90;    # ステータスコード: note on
use constant    MIDI_VELOCITY           => 100;     # ベロシティ

my $console_in = new Win32::Console(STD_INPUT_HANDLE);

my $midi = new Win32API::MIDI;
my $midi_out = new Win32API::MIDI::Out      or die $midi->OutGetErrorText;

my $keybord = new Keybord;

while (1){
    my ($type, $is_down, undef, $vkey, undef, $char) = $console_in->Input;
    next    if $type != 1;          # キー入力以外のイベントの場合
    last    if $vkey == VKEY_END;   # 終了キーが押された場合
    
    # オクターブ移動
    $keybord->moveOctave('up'), next    if $vkey == VKEY_OCTAVE_UP && $is_down;
    $keybord->moveOctave('down'), next  if $vkey == VKEY_OCTAVE_DOWN && $is_down;
    
    my $key = $keybord->keyTurn(chr $char, $is_down);
    next    unless defined $key;
    $midi_out->ShortMsg(
        (($is_down ? 100 : 0) << 16) |
        ($key << 8) |
        MIDI_STATUS_NOTE_ON)
            or die $midi_out->GetErrorText;
}

$midi_out->Close;

__END__

Yahoo! ブログの転載機能2006年12月07日 03時04分37秒

TB どもです。やっと話が見えてきました。

なるほど。Yahoo! ブログには、Yahoo! ブログシステムローカルで機能する「転載」機能が存在し、転載したい記事の「転載」ボタンを押すだけで、自分の Yahoo! ブログに記事を転載できるようになっている。この機能を中心とした話題だったわけですね。

昔、試しに使っていてそのままにしていた Yahoo! ブログがあったので、自分のブログだけで転載機能を試してみました。

自分で試して見て解った点を以下に列挙します。

  • 転載機能を用いてコピーした記事は、編集できない。転載時の内容がそのまま保たれる。
  • 上記で転載元と転載先の内容が異なるのは、転載を行ってから転載元を編集しなおした為。この場合、現時点では、転載元と転載先の記事は同期しない模様 (つまり、ハードリンクのようなものではなく、あくまでコピーであるということ)。
  • オリジナルの記事を作成もしくは編集時に、転載機能を許可するか拒否するかを設定可能。
  • 転載機能によってコピーした記事は編集できないため、転載を拒否する設定に変更することはできないっぽい。転載元の記事がコメント・トラックバックを受け付ける設定の場合は、これらも拒否する設定には変更できないっぽい。

この機能を実装した Yahoo! は、サーバーのキャパシティによほどの自身があるのでしょう。。。

個人的な見解を申し上げます。

Yahoo! ブログが機能として提供している以上、Yahoo! ブログシステム内の仲間同士で、この機能を用いて記事の転載を行いあうこと自体は、多くの場合、それほど問題は無いように思います。

ただ、「転載」という用語自体はごく一般的なものですので、「ご自由に転載してください」のような記述をした場合、Yahoo! ブログ以外のブログ等をもつ人が、手動で記事をコピーし、転載する行為も認めることになるという点には、注意する必要があるかもしれません (Yahoo! ブログが SNS だったならば、問題にはならないのですが)。

あとは、まぁ、各自の良識の範囲内で使われればよろしいのではないかと思います。

経営情報2006年12月07日 11時00分42秒

経営情報学についてはあんまり興味のないおいらなのですが、上記記事が結構面白かったので、著者の方のサイトを覗いてみました。

経営と情報に関する私の主張の中で、ERPパッケージ批判という記事があり、まだこれぐらいしか目を通していないのですが、これがまた面白い。要チェックです。

何この妄想。2006年12月07日 11時44分22秒

小飼氏による記事経由。

                            この記事を読む前に言っておくッ!
                        おれは今この記事を流し読みだが読んできた
                  い…いや…読んだというよりはまったく理解を超えていたのだが……
         ,. -‐'''''""¨¨¨ヽ
         (.___,,,... -ァァフ|          あ…ありのまま 今 起こった事を話すぜ!
          |i i|    }! }} //|
         |l、{   j} /,,ィ//|       『部下を教育する話だと思ったら
        i|:!ヾ、_ノ/ u {:}//ヘ        実は馴れ合いごっこによる部下いびりの話だった』
        |リ u' }  ,ノ _,!V,ハ |
       /´fト、_{ル{,ィ'eラ , タ人        な… 何を言ってるのか わからねーと思うが
     /'   ヾ|宀| {´,)⌒`/ |<ヽトiゝ        おれも何が「ひとつ上」なのかわからなかった…
    ,゙  / )ヽ iLレ  u' | | ヾlトハ〉
     |/_/  ハ !ニ⊇ '/:}  V:::::ヽ        頭がどうにかなりそうだった…
    // 二二二7'T'' /u' __ /:::::::/`ヽ
   /'´r -―一ァ‐゙T´ '"´ /::::/-‐  \    勘違いだとか作り話だとか
   / //   广¨´  /'   /:::::/´ ̄`ヽ ⌒ヽ    そんなチャチなもんじゃあ 断じてねえ
  ノ ' /  ノ:::::`ー-、___/::::://       ヽ  }
_/`丶 /:::::::::::::::::::::::::: ̄`ー-{:::...       イ  もっと恐ろしいものの片鱗を味わったぜ…

つか、こういうことが本当に行われて、上司を疑えないようじゃ、単なる馬鹿だと思うわけだが。おいらがこの「坂本君」だったら、この時点でもう辞めることを前提に出力 10% ぐらいでしか仕事しなくなっちゃうけどね。

ただまぁ、こういう形で、上司と結託して部下をいびるどーしよーも無い人間は結構いるよーというケーススタディとしてはそれなりに参考になった。現場の上司がこういうことになっちゃっているような会社はまず間違いなく古き悪しき序列社会を強いる会社であり、あなたの実力がまともに評価されて反映されることは有り得ないので、心の距離を物凄く長めにとることにするか、若しくはさっさと辞めることを考えた方がよろしいかと思われ。

BlueScreen Screen Saver2006年12月07日 14時06分56秒

この発想はなかったわ。。。しかも MS 純正だし \(^O^)/ 。

蛇足。

例えば、ブルースクリーンが出ていたら、そんなPCは誰しも触りたくないだろう。離席のたびに人だかりができて、むしろ防犯対策になるかもしれない。

「先輩の PC、ブルースクリーンになっちゃってたから、再起動しておきましたよ♪」とか言い出すおせっかいな後輩による被害が続出する悪寒。。。w