2023年1月22日日曜日

C#でピアノを作ろう (2022/1/22版)

  以前、C#でMIDIを遊ぶプログラムを作りました。これを少し更新してみました。


 ボタンで作成していた鍵盤をPictureBoxに変えて、キーイベントを追加しました。そのうち自動演奏機能も付けようとしてますが、いつになるかは気分次第。


 ソースコードのご利用はご自由に。


using System;

using System.Runtime.InteropServices;

using System.Drawing;

using System.Windows.Forms;

using System.Collections.Generic;


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 MenuStrip ms;

private ToolStripMenuItem[][] tsmi;


private Label lbl;

private ComboBox cbx;


private Panel pnl;

private PictureBox pbx;


private Dictionary<Keys, int> keyAssign;


private bool[] keyFlag;


private int mouseX;

private int mouseY;


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

public FormPiano()

{

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

this.ClientSize = new Size( 840, 240);

this.Text = "Hello World!";


this.Load += new EventHandler( this.Form_Load);

this.Closed += new EventHandler( this.Form_Closed);


this.KeyPreview = true;

this.KeyDown += new KeyEventHandler( this.Form_KeyDown);

this.KeyUp += new KeyEventHandler( this.Form_KeyUp);


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

this.CreateMenu();


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

this.CreateCtrl();

}


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

private void Form_Load( object sender, EventArgs e)

{

Console.WriteLine( "Form_Load");


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

this.pnl.AutoScrollPosition = new Point( 280 * 4, 0);

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

this.keyAssign = new Dictionary<Keys, int>();


this.keyAssign.Add( Keys.A, 60);

this.keyAssign.Add( Keys.W, 61);

this.keyAssign.Add( Keys.S, 62);

this.keyAssign.Add( Keys.E, 63);

this.keyAssign.Add( Keys.D, 64);

this.keyAssign.Add( Keys.F, 65);

this.keyAssign.Add( Keys.T, 66);

this.keyAssign.Add( Keys.G, 67);

this.keyAssign.Add( Keys.Y, 68);

this.keyAssign.Add( Keys.H, 69);

this.keyAssign.Add( Keys.U, 70);

this.keyAssign.Add( Keys.J, 71);

this.keyAssign.Add( Keys.K, 72);

this.keyAssign.Add( Keys.O, 73);

this.keyAssign.Add( Keys.L, 74);


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

if( midiOutOpen( ref this.hMidi, MIDI_MAPPER, 0, 0, 0) != MMSYSERR_NOERROR)

{

MessageBox.Show( "midiOutOpen error");

Application.Exit();

}


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

this.keyFlag = new bool[128];


for( int i = 0; i < this.keyFlag.Length; i++)

{

this.keyFlag[i] = false;

}


this.mouseX = 0;

this.mouseY = 0;

}

private void Form_Closed( object sender, EventArgs e)

{

Console.WriteLine( "Form_Closed");


midiOutClose( this.hMidi);

}


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

/* Menu */

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

private void CreateMenu()

{

this.ms = new MenuStrip();


this.tsmi = new ToolStripMenuItem[1][];

this.tsmi[0] = new ToolStripMenuItem[2];


for( int i = 0; i < this.tsmi.Length; i++)

{

for( int j = 0; j < this.tsmi[i].Length; j++)

{

this.tsmi[i][j] = new ToolStripMenuItem();

}

}


this.tsmi[0][0].Text = "File (&F)";

this.tsmi[0][1].Text = "Exit (&X)";


for( int i = 0; i < this.tsmi.Length; i++)

{

this.ms.Items.Add( this.tsmi[i][0]);


for( int j = 1; j < this.tsmi[i].Length; j++)

{

this.tsmi[i][0].DropDownItems.Add( this.tsmi[i][j]);

this.tsmi[i][j].Click += new EventHandler( this.Menu_Click);

}

}


this.Controls.Add( this.ms);

this.MainMenuStrip = ms;

}


private void Menu_Click( object sender, EventArgs e)

{

Console.WriteLine( sender.ToString());


if( sender.ToString() == "Exit (&X)")

{

Application.Exit();

}

}


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

/* Control */

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

private void CreateCtrl()

{

this.lbl = new Label();

this.lbl.SetBounds( 20, this.ms.Height, 60, 20);

this.lbl.Text = "program";

this.Controls.Add( this.lbl);


this.cbx = new ComboBox();

this.cbx.SetBounds( 80, this.ms.Height, 60, 20);

this.cbx.DropDownStyle = ComboBoxStyle.DropDownList;

this.Controls.Add( this.cbx);


for( int i = 0; i < 128; i++)

{

this.cbx.Items.Add( i);

}


this.cbx.SelectedIndex = 0;

this.cbx.SelectedIndexChanged += new EventHandler( this.cbx_SelectedIndexChanged);

this.pnl = new Panel();

this.pnl.SetBounds( 20, this.ms.Height + 20, 800, 200 - this.ms.Height);

this.pnl.Anchor = ( AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom);

this.pnl.BorderStyle = BorderStyle.FixedSingle;

this.pnl.AutoScroll = true;

this.Controls.Add( this.pnl);


this.pbx = new PictureBox();

this.pbx.SetBounds( 0, 0, 3000, 160);

this.pbx.BorderStyle = BorderStyle.FixedSingle;

this.pbx.Paint += new PaintEventHandler( this.pbx_Paint);

this.pbx.MouseDown += new MouseEventHandler( this.pbx_MouseDown);

this.pbx.MouseMove += new MouseEventHandler( this.pbx_MouseMove);

this.pbx.MouseUp += new MouseEventHandler( this.pbx_MouseUp);

this.pnl.Controls.Add( this.pbx);

}


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

/* PictureBox Paint */

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

private void pbx_Paint( object sender, PaintEventArgs e)

{

Graphics g = e.Graphics;


Rectangle rect = new Rectangle();


Font fnt = new Font( "Arial", 8);

StringFormat sf = new StringFormat();

sf.Alignment = StringAlignment.Center;

sf.LineAlignment = StringAlignment.Center;


/*----- White Keys -----*/

for( int i = 0; i < 128; i++)

{

int q = i / 12;

int r = i % 12;


if( r == 0 || r == 2 || r == 4 || r == 5 || r == 7 || r == 9 || r == 11)

{

rect.X = 280 * q + 20 * ( r < 5 ? r : r + 1);

rect.Y = 0;

rect.Width = 40;

rect.Height = 160;


Brush b = ( this.keyFlag[i] ? Brushes.Pink : Brushes.White);

Pen p =  Pens.Black;


g.FillRectangle( b, rect);

g.DrawRectangle( p, rect);


rect.Y = 120;

rect.Height = 40;

g.DrawString( i.ToString(), fnt, Brushes.Gray, rect, sf);


foreach( KeyValuePair<Keys, int> kvp in this.keyAssign)

{

if( i == kvp.Value)

{

rect.Y = 80;

g.DrawString( ( (char)kvp.Key).ToString(), fnt, Brushes.Gray, rect, sf);

}

}

}

}


/*----- Black Keys -----*/

for( int i = 0; i < 128; i++)

{

int q = i / 12;

int r = i % 12;


if( r == 1 || r == 3 || r == 6 || r == 8 || r == 10)

{

rect.X = 280 * q + 20 * ( r < 5 ? r : r + 1) + 5;

rect.Y = 0;

rect.Width = 30;

rect.Height = 80;


Brush b = ( this.keyFlag[i] ? Brushes.Pink : Brushes.Black);

Pen p =  Pens.White;


g.FillRectangle( b, rect);

g.DrawRectangle( p, rect);


rect.Y = 40;

rect.Height = 40;

g.DrawString( i.ToString(), fnt, Brushes.Gray, rect, sf);

foreach( KeyValuePair<Keys, int> kvp in this.keyAssign)

{

if( i == kvp.Value)

{

rect.Y = 0;

g.DrawString( ( (char)kvp.Key).ToString(), fnt, Brushes.Gray, rect, sf);

}

}

}

}

}


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

/* Mouse Event */

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

private void pbx_MouseDown( object sender, MouseEventArgs e)

{

Console.WriteLine( "pbx_MouseDown");


byte key = this.PositionToKey( e.X, e.Y);


if( ( key != 255) && !this.keyFlag[key])

{

this.keyFlag[key] = true;

this.NoteOn( key);


this.mouseX = e.X;

this.mouseY = e.Y;

}


this.pbx.Invalidate();

}


private void pbx_MouseMove( object sender, MouseEventArgs e)

{

if( e.Button != MouseButtons.None)

{

byte key1 = this.PositionToKey( this.mouseX, this.mouseY);

byte key2 = this.PositionToKey( e.X, e.Y);


if( key1 != key2)

{

Console.WriteLine( "pbx_MouseMove");


this.keyFlag[key1] = false;

this.NoteOff( key1);


this.keyFlag[key2] = true;

this.NoteOn( key2);


this.mouseX = e.X;

this.mouseY = e.Y;


this.pbx.Invalidate();

}

}

}


private void pbx_MouseUp( object sender, MouseEventArgs e)

{

Console.WriteLine( "pbx_MouseUp");


byte key = this.PositionToKey( e.X, e.Y);


if( ( key != 255) && this.keyFlag[key])

{

this.keyFlag[key] = false;

this.NoteOff( key);

}


this.pbx.Invalidate();

}


private byte PositionToKey( int x, int y)

{

Rectangle rect = new Rectangle();


/*----- Black Keys -----*/

for( int i = 0; i < 128; i++)

{

int q = i / 12;

int r = i % 12;


if( r == 1 || r == 3 || r == 6 || r == 8 || r == 10)

{

rect.X = 280 * q + 20 * ( r < 5 ? r : r + 1) + 5;

rect.Y = 0;

rect.Width = 30;

rect.Height = 80;


if( rect.X <= x && x < rect.X + rect.Width && rect.Y <= y && y <= rect.Y + rect.Height)

{

return (byte) i;

}

}

}


/*----- White Keys -----*/

for( int i = 0; i < 128; i++)

{

int q = i / 12;

int r = i % 12;


if( r == 0 || r == 2 || r == 4 || r == 5 || r == 7 || r == 9 || r == 11)

{

rect.X = 280 * q + 20 * ( r < 5 ? r : r + 1);

rect.Y = 0;

rect.Width = 40;

rect.Height = 160;


if( rect.X <= x && x < rect.X + rect.Width && rect.Y <= y && y <= rect.Y + rect.Height)

{

return (byte) i;

}

}

}


return 255;

}


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

/* Key Event */

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

private void Form_KeyDown( object sender, KeyEventArgs e)

{

Console.WriteLine( "Form1_KeyDown" + "\t" + e.KeyCode.ToString());


foreach( KeyValuePair<Keys, int> kvp in this.keyAssign)

{

if( e.KeyCode == kvp.Key)

{

byte key = (byte)kvp.Value;


if( !this.keyFlag[key])

{

this.keyFlag[key] = true;

this.NoteOn( key);

}


this.pbx.Invalidate();


return;

}

}

}


private void Form_KeyUp( object sender, KeyEventArgs e)

{

Console.WriteLine( "Form1_KeyUp" + "\t" + ( (int) e.KeyCode).ToString());


foreach( KeyValuePair<Keys, int> kvp in this.keyAssign)

{

if( e.KeyCode == kvp.Key)

{

byte key = (byte)kvp.Value;


if( this.keyFlag[key])

{

this.keyFlag[key] = false;

this.NoteOff( key);

}


this.pbx.Invalidate();

}

}

}


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

/* ComboBox */

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

private void cbx_SelectedIndexChanged(object sender, System.EventArgs e)

{

byte prg = (byte) this.cbx.SelectedIndex;

Console.WriteLine( "cbx_SelectedIndexChanged " + prg.ToString());


this.ProgramChange( prg);

}


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

/* Note On/Off */

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

private void NoteOn( byte key)

{

byte ch = 0; //(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);


DateTime dt = DateTime.Now;

Console.WriteLine( dt.ToString( "hh:mm:ss.fff") + "\t" + "NoteOn" + "\t" + key.ToString());

}

private void NoteOff( byte key)

{

byte ch = 0; //(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);


DateTime dt = DateTime.Now;

Console.WriteLine( dt.ToString( "hh:mm:ss.fff") + "\t" + "NoteOff" + "\t" + key.ToString());

}


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

/* Program Change */

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

private void ProgramChange( byte prg)

{

byte ch = (byte) 0;


uint msg;

msg = (uint)( ( prg << 8) + 0xc0 + ch);

midiOutShortMsg( this.hMidi, msg);

}

}



0 件のコメント:

コメントを投稿