2020年9月6日日曜日

C#で、もう1回、MIDI その8

  今回は、MIDIの演奏のWindowsプログラムです。これまでの内容を組み合わせただけです。

 本当はMIDI編集用のプログラムも作ろうかと思ったのですが、いい感じのユーザーインターフェースが思いつかなかったので。。。

 気が向いたら、またいろいろ追加、更新しようと思います。


 以下が今回のC#のソースコードです。これまでに作った"MidiAPI.cs", "MidiEvent.cs", "MidiFile.cs", "MidiPlayer.cs"も使うので、その5ファイルを合わせてコンパイルしてください。


 ソースコードはご自由にご利用ください。ただし、趣味のプログラムなので保証はありません。コメントとかも適当です。プログラミングを勉強する方のご参考にでもなれば。


------------------------------

using System;

using System.Drawing;

using System.Windows.Forms;


class Program

{

[STAThread]


static void Main()

{

Application.Run( new FormMidiView());

}

}


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

class FormMidiView : Form

{

private MenuStrip ms;

private ToolStripMenuItem[][] tsmi;


private ComboBox cbx;

private TextBox tbx;

private ListBox lbx;


MidiPlayer player;


private int TimeDivision;

private MidiEvent[][] Track;

private MidiEvent[][] trk; //Track再生用


private Timer tmr;


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

public FormMidiView()

{

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

/* コントロールの作成 */

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

this.SuspendLayout();


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

this.ClientSize = new Size( 400, 300);

this.Text = "MidiView";


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

this.FormClosed += new FormClosedEventHandler( this.Form_FormClosed);


this.AllowDrop = true;

this.DragEnter += new DragEventHandler( this.Form_DragEnter);

this.DragDrop += new DragEventHandler( this.Form_DragDrop);


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

this.CreateMenu();


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

this.cbx = new ComboBox();

this.cbx.SetBounds( 0, this.ms.Height, 100, 40);

this.cbx.Anchor = AnchorStyles.Left | AnchorStyles.Top;

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

this.Controls.Add( this.cbx);


this.tbx = new TextBox();

this.tbx.SetBounds( 0, this.ms.Height + this.cbx.Height, this.ClientSize.Width, 40);

this.tbx.BorderStyle = BorderStyle.None;

this.tbx.ReadOnly = true;

this.tbx.Anchor = AnchorStyles.Left | AnchorStyles.Top;

this.tbx.Text = "Index\tTime\tEvent\tParam1\tParam2\tType\tLength";

this.Controls.Add( this.tbx);


this.lbx = new ListBox();

this.lbx.SetBounds( 0, this.ms.Height + this.cbx.Height + this.tbx.Height, this.ClientSize.Width, this.ClientSize.Height - this.ms.Height - this.cbx.Height - this.tbx.Height);

this.lbx.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom;

this.Controls.Add( this.lbx);


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

/* Timer */

/* 表示更新用のタイマー。ちなみに、MIDIを再生しているThreadからListBoxの表示を変更するにはDelegateが必要。面倒くさい。 */

this.tmr = new Timer();

this.tmr.Tick += new EventHandler( this.timer_Tick);

this.tmr.Interval = 100;

this.tmr.Enabled = false;


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

this.ResumeLayout();

}


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

private void CreateMenu()

{

this.ms = new MenuStrip();


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

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

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


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

{

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

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


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

{

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

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

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

}

}


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

this.tsmi[0][1].Text = "Open (&O)";

this.tsmi[0][2].Text = "Close (&C)";

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

this.tsmi[1][0].Text = "Midi (&M)";

this.tsmi[1][1].Text = "TimeDivision";

this.tsmi[1][2].Text = "Play";

this.tsmi[1][3].Text = "Play Track";

this.tsmi[1][4].Text = "Stop";


this.Controls.Add( this.ms);

this.MainMenuStrip = ms;

}


private void Menu_Click( object sender, EventArgs e)

{

if( sender == this.tsmi[0][1])

{

OpenFileDialog ofd = new OpenFileDialog();

ofd.Filter = "file|*.mid";

if( ofd.ShowDialog() == DialogResult.OK)

{

this.open( ofd.FileName);

}

}

else if( sender == this.tsmi[0][2])

{

this.player.Stop();

this.tmr.Enabled = false;


this.cbx.Items.Clear();

this.lbx.Items.Clear();


this.TimeDivision = 0;

this.Track = null;

}

else if( sender == this.tsmi[0][3])

{

this.Close();

}

else if( sender == this.tsmi[1][1])

{

MessageBox.Show( "TimeDivision : " + this.TimeDivision.ToString() + " tick");

}

else if( sender == this.tsmi[1][2])

{

if( this.Track == null)

{

return;

}


int i = this.cbx.SelectedIndex;

int j = this.lbx.SelectedIndex;

int tm = this.Track[i][j].Time * 500 / this.TimeDivision;


this.player.Play( this.TimeDivision, this.Track, tm);

this.tmr.Enabled = true;

}

else if( sender == this.tsmi[1][3])

{

if( this.Track == null)

{

return;

}


int i = this.cbx.SelectedIndex;

int j = this.lbx.SelectedIndex;

int tm = this.Track[i][j].Time * 500 / this.TimeDivision;


this.trk = new MidiEvent[1][];

this.trk[0] = new MidiEvent[this.Track[i].Length];


for( int k = 0; k < this.trk[0].Length; k++)

{

this.trk[0][k] = this.Track[i][k];

}


this.player.Play( this.TimeDivision, this.trk, tm);

this.tmr.Enabled = true;

}

else if( sender == this.tsmi[1][4])

{

if( this.Track == null)

{

return;

}


this.player.Stop();

this.tmr.Enabled = false;

}

}


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

private void Form_Load( object sender, EventArgs e)

{

Console.WriteLine( "Form_Load");


this.player = new MidiPlayer();


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

string[] args = Environment.GetCommandLineArgs();


if( 1 < args.Length)

{

this.open( args[1]);

}

}

private void Form_FormClosed( object sender, EventArgs e)

{

Console.WriteLine( "Form_FormClosed");


this.player.Stop();

this.tmr.Enabled = false;

}


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

private void Form_DragEnter( object sender, DragEventArgs e)

{

if( e.Data.GetDataPresent( DataFormats.FileDrop))

{

e.Effect = DragDropEffects.Move;

}

}

private void Form_DragDrop( object sender, DragEventArgs e)

{

Console.WriteLine( "Form_DragDrop");


string[] filename = (string[]) e.Data.GetData( DataFormats.FileDrop, false);


this.open( filename[0]);

}


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

private void cbx_SelectedIndexChanged( object sender, EventArgs e)

{

Console.WriteLine( "cbx_SelectedIndexChanged");


this.lbx.Items.Clear();


int index = this.cbx.SelectedIndex;


for( int i = 0; i < this.Track[index].Length; i++)

{

byte evt = this.Track[index][i].Event;


string str = i.ToString() + "\t";

str += this.Track[index][i].Time.ToString() + "\t";

str += this.Track[index][i].Event.ToString( "x") + "\t";


if( evt < 0xf0)

{

ChannelEvent ce = (ChannelEvent) this.Track[index][i];


str += ce.Param1.ToString() + "\t";

str += ce.Param2.ToString() + "\t";

str += "\t";

str += "\t";

}

else if( evt == 0xf0 || evt == 0xf7)

{

SystemEvent se = (SystemEvent) this.Track[index][i];


str += "\t";

str += "\t";

str += "\t";

str += se.Data.Length.ToString() + "\t";

}

else if( evt == 0xff)

{

MetaEvent me = (MetaEvent) this.Track[index][i];


str += "\t";

str += "\t";

str += me.Type.ToString() + "\t";

str += me.Data.Length.ToString() + "\t";

}


this.lbx.Items.Add( str);

}


this.lbx.SelectedIndex = 0;

}


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

private void timer_Tick( object sender, EventArgs e)

{

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

if( this.player.IsPlaying() == false)

{

this.tmr.Enabled = false;

return;

}


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

long tm = this.player.PlayTime;

int index = this.cbx.SelectedIndex;

int first = 0;

int last = this.Track[index].Length - 1;


while( 1 < last - first)

{

int next = ( first + last) / 2;


if( this.Track[index][next].Time * 500 / this.TimeDivision < tm)

{

first = next;

}

else

{

last = next;

}

}


if( first < this.lbx.Items.Count)

{

// cbxの選択が変更されると、Play中でもlbxが一度Clearされる

// そのため、このif文がないとタイミングによってエラーになる


this.lbx.SelectedIndex = first;

}

}


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

private void open( string filename)

{

this.player.Stop();

this.tmr.Enabled = false;


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

try

{

MidiFileReader.Read( filename, ref this.TimeDivision, ref this.Track);

}

catch( Exception e)

{

MessageBox.Show( "File Read Error");

Console.WriteLine( e.ToString());

}


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

this.cbx.Items.Clear();


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

{

this.cbx.Items.Add( "Track " + i.ToString());

}


this.cbx.SelectedIndex = 0;

}

}


0 件のコメント:

コメントを投稿