øâ[‰æ‘œ]
#!/usr/bin/perl
package Keybord;
use strict;
use Win32::Console;
# Œ®”Ղ̃L[Š„‚è“–‚Ä
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 -)];
# Œ®”Õ‚Ì‚’¼•\Ž¦ˆÊ’u
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 ];
# Œ®”Õ‚Ì…•½•\Ž¦ˆÊ’u
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;
# Œ®”Õ‚ÌF
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;
# Œ®”Õ‚Ìʼn‚̃L[‚ÉŠ„‚è“–‚Ä‚ç‚ê‚éƒm[ƒg’l (ƒfƒtƒHƒ‹ƒg’l)
use constant FIRST_NOTE_LOW => 48;
use constant FIRST_NOTE_HIGH => 65;
# ƒIƒNƒ^[ƒuƒVƒtƒg‚ŕω»‚·‚éƒm[ƒg’l
use constant OCTAVE => 12;
# ƒIƒNƒ^[ƒuƒVƒtƒg‚Ì臒l
use constant NOTE_UNDER_LIMIT => 0;
use constant NOTE_UPPER_LIMIT => 127 - scalar(@{&KEYBORD_MAP_WHITE_HIGH}) * 2;
# Œ®”Ղ̃L[‚ɑ΂·‚éŠeŽíî•ñ (ƒm[ƒg’l·•ªAƒCƒ“ƒfƒNƒXA”’Œ® or •Œ®A‰Ÿ‰º’†‚©”Û‚©)
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);
}
# ƒRƒ“ƒ\[ƒ‹o—̓IƒuƒWƒFƒNƒg
our $console_out = new Win32::Console(STD_OUTPUT_HANDLE);
# ƒpƒbƒP[ƒWƒ[ƒJƒ‹ŠÖ”
my ($displayKeybord);
# ƒIƒuƒWƒFƒNƒg¶¬ - ‰æ–Êì‚è
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, # ‰ºŒ®”Õ‚Ìʼn‚̃L[‚̃m[ƒg’l
'first note high' => FIRST_NOTE_HIGH, # ㌮”Õ‚Ì (ˆÈ‰º—ª)
};
bless $self, $class
}
# ƒfƒXƒgƒ‰ƒNƒ^ - ‰æ–ÊÁ‹Ž
sub DESTROY {
$console_out->Cls($ATTR_NORMAL);
}
# ƒL[ on/off Ø‚è‘Ö‚¦ - ‘Ήž‚·‚éƒm[ƒg’l‚ð•Ô‚·
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;
# ƒŠƒs[ƒg–hŽ~‚̈×A‰Ÿ‚µ‘±‚¯‚Ä‚¢‚éŠÔ‚̓m[ƒg’l‚ð•Ô‚³‚È‚¢
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]);
# ƒnƒCƒ‰ƒCƒg‚ÌØ‚è‘Ö‚¦
$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"}
}
# ƒIƒNƒ^[ƒu‚ðã‚°‚é / ‰º‚°‚é
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;
}
# ‰æ–Êì‚è - ƒL[ƒ{[ƒh•`‰æ
$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;
# ‰¼‘zƒL[
use constant VKEY_END => 27; # I—¹ƒL[ : [ESC]
use constant VKEY_OCTAVE_UP => 38; # ƒIƒNƒ^[ƒu‚ðã‚°‚é : ƒJ[ƒ\ƒ‹ƒL[ [ª]
use constant VKEY_OCTAVE_DOWN => 40; # ƒIƒNƒ^[ƒu‚ð‰º‚°‚é : ƒJ[ƒ\ƒ‹ƒL[ [«]
# MIDI ƒCƒxƒ“ƒg
use constant MIDI_STATUS_NOTE_ON => 0x90; # ƒXƒe[ƒ^ƒXƒR[ƒh: note on
use constant MIDI_VELOCITY => 100; # ƒxƒƒVƒeƒB
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; # ƒL[“ü—͈ȊO‚̃Cƒxƒ“ƒg‚Ìê‡
last if $vkey == VKEY_END; # I—¹ƒL[‚ª‰Ÿ‚³‚ꂽê‡
# ƒIƒNƒ^[ƒuˆÚ“®
$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__
ù¾ºÒÝÄ‚ð‚·‚é