2017年5月4日木曜日

C#でMIDI その3

 C#でMIDIの続きです。

 前回はフォームバージョンにしてみました。
 実行してみると分かりますが、音を鳴らしている間は、フォームが動きません。これは、フォームの処理と音を鳴らす処理が片方ずつ処理されているからです。

 というわけで、今回は、マルチスレッド化して、音を鳴らしている間もフォームが動かせるようにしてみます。マルチスレッドについては、適当にインターネットで調べてみてください。

 マルチスレッドのやり方も複数あるみたいですが、今回はスタンダードっぽいやり方をしてみました。本当はもっといいやり方があるかもしれません。そこは趣味のプログラムということで、ご勘弁。


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

class Program
{
[STAThread]

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

class FormMidi : Form
{
/*--------------------------------------------------*/
[DllImport( "Winmm.dll")]
extern static uint midiOutGetNumDevs();

[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);

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

private const uint MMSYSERR_NOERROR = 0;

private const uint MMSYSERR_BADDEVICEID = 2;
private const uint MMSYSERR_ALLOCATED = 4;
private const uint MMSYSERR_NOMEM = 7;
private const uint MMSYSERR_INVALPARAM = 11;
private const uint MMSYSERR_NODEVICE = 68;

private const uint MMSYSERR_INVALHANDLE = 5;
private const uint MIDIERR_STILLPLAYING = 65;

private const uint MIDI_MAPPER = 0xffffffff;

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

Thread thread;
long hMidi;

Button btn_play;
Button btn_stop;

public FormMidi()
{
this.ClientSize = new Size( 240, 120);

this.Text = "Midi";
this.Load += new EventHandler( this.FormMidi_Load);
this.Closed += new EventHandler( this.FormMidi_Closed);

this.btn_play = new Button();
this.btn_play.SetBounds( 20, 40, 90, 40);
    this.btn_play.Font = new Font( "Arial", 12);
this.btn_play.Text = "Play";
this.btn_play.Click += new EventHandler( this.btn_play_Click);
this.Controls.Add( this.btn_play);

this.btn_stop = new Button();
this.btn_stop.SetBounds( 130, 40, 90, 40);
    this.btn_stop.Font = new Font( "Arial", 12);
this.btn_stop.Text = "Stop";
this.btn_stop.Click += new EventHandler( this.btn_stop_Click);
this.Controls.Add( this.btn_stop);
}

/*--------------------------------------------------*/
/* Load */
/*--------------------------------------------------*/
private void FormMidi_Load( object sender, EventArgs e)
{
Console.WriteLine( "FormMidi_Load");

ThreadStart ts = new ThreadStart( this.Play);
this.thread = new Thread( ts);
}

/*--------------------------------------------------*/
/* Closed */
/*--------------------------------------------------*/
private void FormMidi_Closed( object sender, EventArgs e)
{
Console.WriteLine( "FormMidi_Closed");

if( this.thread.IsAlive == true)
{
this.PlayStop();
}
}

/*--------------------------------------------------*/
/* Clicked */
/*--------------------------------------------------*/
private void btn_play_Click( object sender, EventArgs e)
{
Console.WriteLine( "btn_play_Click");

if( this.thread.IsAlive == false)
{
this.PlayStart();
}
}

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

if( this.thread.IsAlive == true)
{
this.PlayStop();
}
}

/*--------------------------------------------------*/
/* PlayStart */
/*--------------------------------------------------*/
private void PlayStart()
{
if( midiOutOpen( ref this.hMidi, MIDI_MAPPER, 0, 0, 0) != MMSYSERR_NOERROR)
{
MessageBox.Show( "midiOutOpen error");
return;
}

ThreadStart ts = new ThreadStart( this.Play);
this.thread = new Thread( ts);
this.thread.IsBackground = true;
this.thread.Start();
}

/*--------------------------------------------------*/
/* Play */
/*--------------------------------------------------*/
private void Play()
{
uint msg;
byte[] keys = new byte[8];

keys[0] = (byte) 60;
keys[1] = (byte) 62;
keys[2] = (byte) 64;
keys[3] = (byte) 65;
keys[4] = (byte) 67;
keys[5] = (byte) 69;
keys[6] = (byte) 71;
keys[7] = (byte) 72;

for( int i = 0; i < keys.Length; i++)
{
Console.WriteLine( "note on " + "\t" + keys[i]);
msg = (uint) ( ( 0x7f << 16) + ( keys[i] << 8) + 0x90);
midiOutShortMsg( this.hMidi, msg);

Thread.Sleep( 500);

Console.WriteLine( "note off" + "\t" + keys[i]);
msg = (uint) ( ( 0x7f << 16) + ( keys[i] << 8) + 0x80);
midiOutShortMsg( this.hMidi, msg);
}

midiOutReset( this.hMidi);
midiOutClose( this.hMidi);
}

/*--------------------------------------------------*/
/* PlayStop */
/*--------------------------------------------------*/
private void PlayStop()
{
this.thread.Abort();

midiOutReset( this.hMidi);
midiOutClose( this.hMidi);
}
}

0 件のコメント:

コメントを投稿