2016年2月14日日曜日

C#でピアノを作ろう

 こう見えて(?)私は音楽好きです。というわけで、暇つぶしに、C#でピアノを作ってみました。

 ボタンを押したら、MIDIの音を鳴らすだけの単純なプログラムです。鍵盤の作り方とかマウスイベントとか、見る人が見ればちょっと面白いかもしれません。

 最近のWindowsはC#のコンパイラを持っているので、誰でも手軽にプログラムができます。

 コンパイル方法は、以下の通り。
(1)以下のソースコードをテキストファイルにコピー、ファイル名を適当に"piano.cs"として保存
(2)コマンドプロンプトで次のコマンドをを実行すると、"piano.exe"ができる
>cd [csc.exeがあるフォルダ]
>csc.exe /target:winexe "[piano.csがあるフォルダ]/piano.cs"

 うまくいかないという方は、周りの詳しそうな人かGoogle先生に聞いてみてください。csc.exeで調べればすぐ分かると思います。

 例によって、保証はありませんが、悪しからず。

 ご自由に遊んでください。


2016/2/27 : キーボードで複数のキーが押せるようにフラグを微修正しました

2016/3/6 : いろいろ微修正

2016/7/24 : すべてキーボードが使えるように修正

2017/5/4 : APIの宣言が32bit OS用だったので、64bit OS用に変更 (気分でキーボードの処理は削除)


using System;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Windows.Forms;

class Program
{
[STAThread]

static void Main()
{
Application.Run( new FormPiano());
}
}

class FormPiano : Form
{
/*--------------------------------------------------*/
/* 使用するAPIの宣言 */
/*--------------------------------------------------*/
[DllImport( "Winmm.dll")]
extern static uint midiOutOpen( ref long lphmo, uint uDeviceID, uint dwCallback, uint dwCallbackInstance, uint dwFlags);

[DllImport( "Winmm.dll")]
extern static uint midiOutClose( long hmo);

[DllImport( "Winmm.dll")]
extern static uint midiOutShortMsg( long hmo, uint dwMsg);

private const uint MMSYSERR_NOERROR = 0;
private const uint MIDI_MAPPER = 0xffffffff;

/*--------------------------------------------------*/

private long hMidi;

private Label[] lbl;
private ComboBox[] cmb;

private Panel pnl;
private Button[] key;

public FormPiano()
{
this.ClientSize = new Size( 840, 240);
this.Text = "Piano";

this.Load += new EventHandler( this.FormPiano_Load);
this.Closed += new EventHandler( this.FormPiano_Closed);

/*--------------------------------------------------*/
/* コントロールの作成 */
/*--------------------------------------------------*/
this.lbl = new Label[2];

for( int i = 0; i < this.lbl.Length; i++)
{
this.lbl[i] = new Label();
this.lbl[i].SetBounds( 180 * i, 0, 60, 40, BoundsSpecified.All);
this.Controls.Add( this.lbl[i]);
}
this.lbl[0].Text = "channel";
this.lbl[1].Text = "program";

this.cmb = new ComboBox[2];

for( int i = 0; i < this.cmb.Length; i++)
{
this.cmb[i] = new ComboBox();
this.cmb[i].SetBounds( 180 * i + 60, 0, 60, 40, BoundsSpecified.All);
this.cmb[i].DropDownStyle = ComboBoxStyle.DropDownList;
this.Controls.Add( this.cmb[i]);
}

this.cmb[0].Items.Add( 0);
this.cmb[0].Items.Add( 9);

this.cmb[0].SelectedIndex = 0;

for( int i = 0; i < 127; i++)
{
this.cmb[1].Items.Add( i);
}

this.cmb[1].SelectedIndex = 0;
this.cmb[1].SelectedIndexChanged += new EventHandler( this.cmb_SelectedIndexChanged);

/*--------------------------------------------------*/
/* 鍵盤(ボタン)の作成 */
/*--------------------------------------------------*/
this.pnl = new Panel();
this.pnl.SetBounds( 0, 40, 840, 200, BoundsSpecified.All);
this.pnl.AutoScroll = true;
this.pnl.Anchor = ( AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom);
this.Controls.Add( this.pnl);

this.key = new Button[128];

int count = 0;

for( int i = 0; i < this.key.Length; i++)
{
this.key[i] = new Button();
this.key[i].Name = i.ToString();
this.key[i].Text = i.ToString();
    this.key[i].TextAlign = ContentAlignment.BottomCenter;
this.pnl.Controls.Add( this.key[i]);

this.key[i].MouseDown += new MouseEventHandler( this.key_MouseDown);
this.key[i].MouseUp += new MouseEventHandler( this.key_MouseUp);
this.key[i].MouseEnter += new EventHandler( this.key_MouseEnter);
this.key[i].MouseLeave += new EventHandler( this.key_MouseLeave);

if( count % 2 == 0)
{
this.key[i].SetBounds( 20 * count, 0, 40, 160, BoundsSpecified.All);
this.key[i].ForeColor = Color.Black;
this.key[i].BackColor = Color.White; //白
}
else
{
this.key[i].SetBounds( 20 * count + 5, 0, 30, 80, BoundsSpecified.All);
this.key[i].ForeColor = Color.White;
this.key[i].BackColor = Color.Black; //黒
this.key[i].BringToFront();
}

count += ( i % 12 == 4 || i % 12 == 11) ? 2 : 1;
}
}

/*--------------------------------------------------*/
/* MIDIデバイスのOpenとClose
/*--------------------------------------------------*/

private void FormPiano_Load( object sender, EventArgs e)
{
Console.WriteLine( "FormPiano_Load");

if( midiOutOpen( ref this.hMidi, MIDI_MAPPER, 0, 0, 0) != MMSYSERR_NOERROR)
{
MessageBox.Show( "midiOutOpen error");
Application.Exit();
}

this.pnl.AutoScrollPosition = new Point( 1120, 0);
}

private void FormPiano_Closed( object sender, EventArgs e)
{
Console.WriteLine( "FormPiano_Closed");

midiOutClose( this.hMidi);
}

/*--------------------------------------------------*/
/* Note On/Off */
/*--------------------------------------------------*/

private void NoteOn( byte key)
{
byte ch = (byte) ( ( this.cmb[0].SelectedIndex == 0) ? 0 : 9);
byte velocity = 0x7f;

uint msg;
msg = (uint)( ( velocity << 16) + ( key << 8) + 0x90 + ch);
midiOutShortMsg( this.hMidi, msg);
}

private void NoteOff( byte key)
{
byte ch = (byte) ( ( this.cmb[0].SelectedIndex == 0) ? 0 : 9);
byte velocity = 0x7f;

uint msg;
msg = (uint)( ( velocity << 16) + ( key << 8) + 0x80 + ch);
midiOutShortMsg( this.hMidi, msg);
}

/*--------------------------------------------------*/
/* プログラム変更 */
/*--------------------------------------------------*/

private void ProgramChange( byte prg)
{
byte ch = (byte) 0;

uint msg;
msg = (uint)( ( prg << 8) + 0xc0 + ch);
midiOutShortMsg( this.hMidi, msg);
}

/*--------------------------------------------------*/
/* コンボボックスの処理 */
/*--------------------------------------------------*/

private void cmb_SelectedIndexChanged(object sender, System.EventArgs e)
{
byte prg = (byte) this.cmb[1].SelectedIndex;
Console.WriteLine( "cmb_SelectedIndexChanged " + prg.ToString());

this.ProgramChange( prg);
}

/*--------------------------------------------------*/
/* マウス処理 */
/*--------------------------------------------------*/

private void key_MouseDown( object sender, MouseEventArgs e)
{
( (Button)sender).Capture = false;

byte key = (byte) int.Parse( ( (Button)sender).Name);
Console.WriteLine( "key_MouseDown " + key.ToString());

this.NoteOn( key);
}

private void key_MouseUp( object sender, MouseEventArgs e)
{
byte key = (byte) int.Parse( ( (Button)sender).Name);
Console.WriteLine( "key_MouseUp " + key.ToString());

this.NoteOff( key);
}

private void key_MouseEnter( object sender, EventArgs e)
{
if ((Control.MouseButtons & MouseButtons.Left) == MouseButtons.Left)
{
this.key_MouseDown( sender, new MouseEventArgs( MouseButtons.Left, 1, 0, 0, 0));
}
}

private void key_MouseLeave( object sender, EventArgs e)
{
if ((Control.MouseButtons & MouseButtons.Left) == MouseButtons.Left)
{
this.key_MouseUp( sender, new MouseEventArgs( MouseButtons.Left, 1, 0, 0, 0));
}
}

/*--------------------------------------------------*/
}

0 件のコメント:

コメントを投稿