昨日の続き

さて、クラナド用お手軽プレイヤーを作る。
まあ、プログラムとしてはお手軽なので練習にはちょうどいい。
最終目標は各曲ループと全曲ループの導入。できればDirectAudioでも。


以下長文。

で、とりあえずWindowsフォーム関係がまとまっていて簡単なC♯から。
こんなのに二時間かかってしまった。// (# ゜Д゜) ムッキー
MSDN Libraryをいちいち読んでるからなんだけど、まあ、前回作ったのでさすがにバグは無い。


以下コード 全文


// clndPlayer.cs
//
// Clannad BGM Player
// v1.00 2005年10月27日
//

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

namespace ClndPlayer{

public class Player
{

public static void Main(string[] args)
{
Application.Run( new MainForm());
}

}

//UI class
public class MainForm : System.Windows.Forms.Form
{

public MainForm()
{
CreateForm(); // form を作成、表示。
}

~MainForm()
{
clndStop();//きちんと停止しておく。
}


public void CreateForm()
{

//Button create ***
play_pauseButton = new Button();
play_pauseButton.Text = "->";
play_pauseButton.Location = new Point(210,10);
play_pauseButton.FlatStyle = FlatStyle.System;
play_pauseButton.Click += new EventHandler( this.play_Clicked ); //play button clicked

stopButton = new Button();
stopButton.Text = "■";
stopButton.Location = new Point( play_pauseButton.Left+ play_pauseButton.Width+ 10, play_pauseButton.Top);// [play] [stop]
stopButton.FlatStyle = FlatStyle.System;
stopButton.Click += new EventHandler( this.stop_Clicked );
// *** Button create


//List Box create ***
string[] songName = new string[]{ "町、時の流れ、人", "渚", "渚〜坂の下の別れ(ゲーム用)", //5
"渚〜坂の下の別れ(MusicMode用)", "それは風のように", "は〜りぃすたーふぃっしゅ","Etude pour les petites supercordes", "TOE(前半)", //10
"TOE(後半)", "TOE", "Etude pour les petites supercordes(short)", "彼女の本気", "資料室のお茶会", //15
"東風 -Tempo up-", "東風 -Afternoon-", "東風", "東風 -Piano-","有意義な時間の過ごし方", //20
"有意義な時間の過ごし方 -Guitar-", "有意義な時間の過ごし方 -Sax-", "馬鹿ふたり", "ダム", "灰燼に帰す", //25
"同じ高みへ", "田舎小径", "日々の遑", "存在", "存在 -Piano-", //30
"存在 -E.Piano-", "遥かな年月 -Piano-", "空に光る", "潮鳴り", "潮鳴り II", //35
"願いが叶う場所", "白詰草", "遥かな年月", "夏時間", "カントリートレイン", //40
"雪野原", "願いが叶う場所 II", "幻想", "幻想II", "月の位相", //45
"無間", "汐", "メグメル", "Ana", "Ana (Long Version)", //50
"-影二つ-", "小さなてのひら", "マ・メール・ロワ"}; //53

songList = new ComboBox();
songList.Location = new Point( 10, 10);
songList.Size = new Size( 200, 20);
songList.DropDownStyle = ComboBoxStyle.DropDownList;
for( int i = 0; i" : //音楽再生中で無いとき。→ 普通に再生。
clndPlay( songList.SelectedIndex );
play_pauseButton.Text = "||";
break;
case "||" : //音楽再生中 → 一時停止する。
clndPause();
play_pauseButton.Text = ">|";
break;
case ">|" : //一時停止中 → 一時停止解除
clndPause();
play_pauseButton.Text = "||";
break;
default:
throw new Exception("play / pause ボタンのラベルがおかしい。");
}
}


//音楽停止
private void stop_Clicked( object sender, EventArgs e)
{
clndStop();
play_pauseButton.Text = "->";
}


//リストが選択された。→選択された曲を再生。
private void song_Selected( object sender, EventArgs e)
{
clndStop();
clndPlay( songList.SelectedIndex );
play_pauseButton.Text = "||"; //忘れずにラベルを変更。
}

// *** イベント用関数


// clndwdll.dll インポート関数 ***
[DllImport("clndwdll.dll")] public static extern int clndPlay(int trNum);
[DllImport("clndwdll.dll")] public static extern int clndStop();
[DllImport("clndwdll.dll")] public static extern int clndPause();

// *** clndwdll.dll インポート関数


//UI : form , button( play/pause, stop), List(title of BGM)
private Button play_pauseButton;
private Button stopButton;
private ComboBox songList;

}

} // namespace ClndPlayer

さて、今回気づいたことはずばり状態管理。
プレイヤーには三つの状態がある。

  • 停止中
  • 一時停止中
  • 再生中

さて、ややこしいのが一時停止中。つまりポーズ。
アマグラマーな自分にはこの程度の状態管理でもややこしくてバグを作ってしまう。
状態管理というのは状態をもらさず把握すること。複雑になると漏れが生じてしまう。

で、問題なのが

  • ボタンのラベル 更新し忘れないか。表示が矛盾して無いか。(これがややこしい。)
  • プレイ状況の管理 どの曲を再生中かとか。

以前作ったときは状態管理変数を導入していた。
でもこれは多くなってくると設定し忘れなどが発生するのでよろしくない。
で、今回はそもそもボタンのラベルがプレイ状況を示しているんだからそれを使おうと考える。
その結果のボタンのラベル。

  • -> 「再生」の意味 このときは何も再生していない。(メディアプレイヤーのような右三角にしたかった。)
  • || 「一時停止」の意、このとき再生中、(メディアプレイヤーに似せてみた。)
  • >| 「一時停止解除」の意、このとき一時停止中。

まあ、わかりづらいけど一時停止なんてめったにしないし、使うのは自分だけだしまあいいや。
ちなみに clndwdllのサンプルプレイヤー様は状態管理変数を使っている。(Form1.frm :46)


以下、状態管理変数ありとなしの比較。


状況管理変数あり。

//再生 一時停止
private void play_Clicked( object sender, EventArgs e)
{
if( playButton.Text == "play")
{
//今は演奏してない。
if( isPause ) //一時停止解除
{
clndPause();
}else{
clndPlay( songList.SelectedIndex );
}
playButton.Text = "pause";
isPause = false;

}else{//一時停止
clndPause();
playButton.Text = "play";
isPause = true;
}

}


管理変数なし。
// play / pause
private void play_Clicked( object sender, EventArgs e)
{
switch( play_pauseButton.Text )
{
case "->" : //音楽再生中で無いとき。→ 普通に再生。
clndPlay( songList.SelectedIndex );
play_pauseButton.Text = "||";
break;
case "||" : //音楽再生中 → 一時停止する。
clndPause();
play_pauseButton.Text = ">|";
break;
case ">|" : //一時停止中 → 一時停止解除
clndPause();
play_pauseButton.Text = "||";
break;
default:
throw new Exception("play / pause ボタンのラベルがおかしい。");
}
}

状態管理変数を作ると何かするたびに変数を設定しないといけないので面倒+忘れたら大変。
というお話でした。


えーっとC++版は…。WindowsAPIの本読み直さないと。orz


追記、ソースを載せるのに>>気泡だとスペースとかが無効にされてしまうのでどうしたものかと思ったが、
なんだ<pre>タグを使えばいいのか。