KBGMを利用した簡単SMF再生コンポーネント
KbgmPlayer Ver1.0

[はじめに]
[特長]
[インストール方法]
[チュートリアル1…コードゼロ編]
[チュートリアル2…手動で曲の登録、再生]
[メソッド・プロパティ・イベントリファレンス]
[使用に当たって]
[謝辞]
[連絡先]

はじめに

このコンポーネントは、Kr.Shinさん作のSMF(Standard Midi File)再生DLL、Kbgm32.dllをラップして、コードゼロ、あるいは最小限で、安定したBGMとしてのMIDI再生を実現するためのコンポーネントです。DelphiにもMediaPlayerというコンポーネントがありますが、不安定で、使いづらい面があるようです。

Kbgm32.dllは、ゲームのBGMとしてMIDIを再生したい場合に非常に手軽に使えるようになっていますが、このコンポーネントを併用することで、更に手軽に、このDLLを利用したアプリケーションの開発ができます。

Delphi4に対応しています。KBGMのバージョンは0.08にて確認しています。


特長


インストール方法

KBGMは、ソースコード付きのコンポーネントです。逆にdcuファイルはつけていませんので、自分のDelphiでコンパイルしてください。

また、インストールするためには、別途Kbgm32.dll(Kr.Shinさん作)をダウンロードしていただく必要があります。これはこのアーカイブには含まれていませんので別途入手してください。このDLLがないとこのコンポーネントも、作成したアプリケーションも動作しません(^^;

まず、解凍した後のファイルを、全てDelphiの検索パスが通っている場所にコピーします(例えばDelphi\Lib\)。また、kbgm32.dllは、WindowsのSystemディレクトリに入れておくといいでしょう。Delphiは設計時にもこのDLLにアクセスしますので、ここに置いておけばどこからでも見えて安心です(これはアプリ製作者だけであって、ユーザがSystemフォルダにkbgm32.dllをインストールする必要がある、というわけではありません。ただし、配布するexeと一緒の場所にkbgm32.dllを配布する必要はもちろんあります)

KbgmBgms.dpkをダブルクリックし、Delphiを起動します。パッケージエディタが起動しますので、上の「コンパイル」のボタンを押してコンパイルし、「インストール」をクリックしてインストールしてください。

※注意 かならず、「設計時のみ」のオプションでコンパイル・インストールしてください。KbgmPlayerは、実行時パッケージとして配布することを認めません。

これで、MiscellaneousのページにTKbgmPlayerがインストールされるはずです。


チュートリアル1…コードゼロ編

では実際に、コードゼロでMIDI再生アプリケーションを作成してみます。

まず、テスト用のアプリケーションを保存するため、新しいフォルダを準備します。で、適当にMIDIファイルを2個ほど準備して、この新しいフォルダに、その2つのMIDIファイルをコピーしてください。また、Kbgm32.dllもコピーしてください。(Kbgm32.dllは、あなたの作成するアプリケーションと共に配布する必要があります)

説明の都合上、2つのMIDIファイルの名前は、Title.midと、Stage.midであるとします。

Delphiを起動して、まずはアプリケーションをさっきのフォルダに保存しましょう。ユニット名はとりあえずUnit1、プロジェクト名はProject1ということで。

では、フォームに、先ほどインストールしたTKbgmPlayerを貼り付けてください。名前はKbgmPlayer1になると思います。

KbgmPlayer1の、AutoLoadMidisプロパティをクリックし、右の「...」ボタンをクリックし、先ほどの2つのMIDIファイルの名前を、拡張子無しで1行に1曲ずつ記入します。例えば、2つのMIDIファイルが、Title.midとMain.midだったのなら、AutoLoadMidisプロパティには、TitleとMainとだけ、2行記入してください。

また、AutoPlayプロパティをtrueにしておきます。

以上です。このアプリケーションを実行してみてください。実行したらすぐに、登録したMIDIファイルが鳴り始めるはずです。

うまくいかない場合は、生成されたProject1.exeというファイルと同じ場所に、本当にMIDIファイルとKbgm32.dllがそろっているか確認してください。

このように、AutoLoadMidisプロパティに記入されたMIDIファイルは、自動的に起動時に読み込まれます。また、AutoPlayプロパティをtrueにしておけば、起動時に自動的に再生も始まります。(曲名はAutoLoadMidisの1番最初に指定されているものになります)

AutoPlayによって再生された曲は自動的にループされますので、ゲームを起動している間にずっと同じ曲をエンドレスに流したい、というだけの場合は、これで完全に完成です。アプリケーション終了の際には自動的にKBGMも、曲のハンドルも解放されます。


チュートリアル2…手動で曲の登録、再生

さて、AutoLoadMidisプロパティに記入された曲は、自動的にフォームのロード時(=KbgmPlayerコンポーネントの初期化時)に読み込まれ、メモリに確保されます。が、もしも1つのアプリケーションで何十曲も曲を使う場合などは、一度に全部読み込むのには問題があることもあるかもしれません。(MIDIファイルは小さいので、たいていのゲームなら、一度に読み込んでもそれほど問題にはならないと思いますが)

KbgmPlayerでは、AutoLoadMidis以外にも、手動で曲を読み込むメソッド、また手動で曲のアンロードをするメソッドも準備してあります。

先ほど作ったアプリケーションを終了し、IDEに戻ってください。KbgmPlayerのAutoLoadMidisプロパティの中身をクリアしましょう。また、AutoPlayプロパティもfalseにします。

曲を読み込むため、今度はフォームのOnCreateメソッドに、次のように記入してください。

procedure TForm1.Form1Create(Sender: TObject);
begin
  with KbgmPlayer1 do begin
    LoadMusic('Stage');
    LoadMusic('Title');
    Play('Stage', true, 0, 0);
  end;
end;

以上です。これでアプリケーションを実行すれば、先ほどと全く同じ動作になります。2つの曲をメモリに読み込んで、再生しているわけです。

また、ボタンを1つ配置して、そこのOnClickイベントハンドラに次のように記入してみましょう。

procedure TForm1.Button1Click(Sender: TObject);
begin
  with KbgmPlayer1 do begin
    if NowPlaying = 'Title' then
      ChangeTo('Stage', 5, true, 0, 0)
    else
      ChangeTo('Title', 5, true, 0, 0);
  end;
end;

実行して、このボタンをクリックしてください。クリックするたびに、現在再生中の曲がフェードアウトして、2つの曲が交互に切り替わるはずです。

同じようにして、別のボタンのイベントハンドラに、StopPlay(0);と書けば、押したときに曲が停止します。StopPlay(3);と書けば、滑らかにフェードアウトしながら止まります。

さて、読み込んだ曲は、次のようにして解放できます。KbgmPlayerには解放前に呼び出されるOnDestroyイベントがありますので、そこに新たにイベントハンドラを設定しましょう。

procedure TForm1.KbgmPlayer1Destroy(Sender: TObject);
begin
  with KbgmPlayer1 do begin
    UnloadMusic('Stage');
    UnloadMusic('Title');
  end;
end;

このように書くことで、必要なくなった曲は任意のタイミングで解放できます。(ただし再生途中の場合は解放できません、当然)

が、実は、これを書く必要はありません(^^; KbgmPlayerは、終了前には勝手に読み込み済みの曲は全部解放してくれますので、いちいちユーザがUnloadMusicを呼び出す必要は滅多にないでしょう。メモリの節約にはなりますが。


メソッド・プロパティ・イベントリファレンス

プロパティ

Enabled
property Enabled: boolean;

このプロパティをtrueにしておかないと、何も再生されません。

アプリケーションには、普通「MIDIを再生する」とか、そういうオプションがあると思いますので、それとこのEnabledプロパティを連動させておいてください。falseにしておけば、KBGMの初期化も行いませんので安心です。

falseからtrueにしたとき、もしもAutoLoadプロパティがtrueで、AutoLoadMidisプロパティに曲が入ってあれば、自動で読み込もうとします。

KBGMの初期化にエラーが発生したときなど、Enabledプロパティはfalseになります。例え強制的にtrueを代入しようとしてもfalseになります。

 

AutoLoadMidis
property AutoLoadMidis: TStringList;

起動時に、AutoLoadプロパティがtrueであれば、自動で読み込む曲のリストです。1行に1曲ずつ記入してください。曲は、拡張子を取った形で入力してください(拡張子は*.mid限定です)。例えば、exeと同じ場所にあるBoss.midを読み込む場合は、Bossとだけ記入してください。

 

AutoLoad
property AutoLoad: boolean;

AutoLoadMidisにある曲を、自動的に読み込むかどうか設定します。デフォルトはtrueです。trueにしてあっても、AutoLoadMidisが空なら何も読み込まれないので、まあ無駄といえば無駄なプロパティですが、一応。

 

TimerResolution [設計時のみ]
property TimerResolution: integer;

KBGMの初期化の時に使用する、マルチメディアタイマーの解像度を設定します。通常はデフォルトのままでいいでしょう。

 

AutoPlay
property AutoPlay: boolean;

trueにしておくと、フォームが生成されると同時に、AutoLoadMidisの1曲目に指定されている曲を、自動でリピート再生し始めます。
falseにしておくと、AutoLoadMidisに登録された曲はメモリ上に読み込まれるだけですので、PlayかChangeToメソッドで再生してください。

デフォルトはfalseです。

 

ThrowException
property ThrowException: boolean;

デフォルトではfalseです。trueにしておくと、一部のエラーに対して、Delphiの例外を生成します。例外を生成する場合は、その処理をしっかり行ってください。処理が甘いとアプリケーションが終了します(^^; MIDIごときでアプリが終了するな! という場合は、このプロパティをfalseにしておけば、とりあえずKBGM関連でアプリか突然エラーを出して終わる、ということはなくなるはずです。

例外の種類は以下のとおりです。

EKbgmException = class(Exception);  //以下の例外の上位クラス
EMidiLoadException = class(EKbgmException); //ファイル名が存在しない、など、LoadMusic関連のエラー
EMidiInitException = class(EKbgmException); //KBGM初期化時のエラー
EMidiNotLoadedException = class(EKbgmException); //Play, ChangeToメソッドでのエラー…登録されていない曲を再生しようとした

 

BaseDirectory [設計時のみ]
property BaseDirectory: String;

デフォルトでは空です。アプリケーションと同じ場所にMIDIファイルがない場合は、ここにそのフォルダの場所を指定します。

例えば、MyProject.exeというゲームを作成していて、その関連の曲は、全部、MyProject.exeと同じ場所の「Musics」とうフォルダにまとめて入っている、という場合は、このBaseDirectoryプロパティに、「Musics\」と入力しておいてください。これは、LoadMusicメソッドを使用するときに、そのパラメータの前に自動的に結合されます。

詳しくは、LoadMusicメソッドの説明を参照してください。

 

NowPlaying [実行時のみ][読み出し専用]
property NowPlaying: String;

現在再生中の曲名を返します。空の場合は停止中です。ただし、フェードアウト処理中の場合にも、NowPlayingはフェードアウト処理中の曲が入っています。
前に再生していた曲がフェードアウト処理中の場合があるので、Play('GameOver', false, 0, 0);などとした場合でも、直後にNowPlaying='GameOver'となるとは限りません。フェードアウト処理中ですぐに再生できない場合は、WaitingMusicプロパティに、待ち状態にある曲として登録されます。

 

NowFading [実行時のみ][読み出し専用]
property NowFading: String;

現在、フェードアウト処理中の場合は、NowFadingプロパティにその曲の名前が入っています。一度ある曲がフェードアウト処理に入ってしまうと、その曲を即座に停止させたり、即座に別の曲の再生を始めたりすることは出来ません(唯一の例外として、KBGM自体を終了したときは曲が即座に止まります)。これはKBGMの仕様らしいのでご了承ください。

NowFading<>''、つまりフェードアウト処理中の場合、WaitingMusicプロパティに、次に再生を待っている曲が入っていることがあります。

 

WaigintMusic [実行時のみ][読み出し専用]
property WaitingMusic: String;

曲がフェードアウト処理中の場合、フェードアウトが完了した直後に再生されるべき曲名が登録されています。フェードアウト処理中なのにこのプロパティが空の場合は、フェードアウトのあと新たに別の曲の再生を始めません。

 

MidiError [実行時のみ][読み出し専用]
property MidiError: boolean;

KBGMの初期化をしようとしたが不可能だったときや、その他エラーが発生したときに、trueに設定されます。MidiError=trueのときは、必ずEnabled = falseになりますので、曲は再生されません。

これがtrueになる原因としては、「MIDIデバイスがインストールされていない」「メモリが足りない」「MIDIを他のアプリケーションが使用している」などが考えられます。

一度MidiErrorがtrueになったら、アプリケーションを再起動して初期化を改めて試みる以外に方法はありません。

 

MusicCount [実行時のみ][読み出し専用]
property MusicCount: integer;

登録されている曲の数を返します。下のMusicsプロパティと併用することで、現在登録されている曲のリストを取得することが可能ですが、ゲーム作成の場では、滅多に使用することはないでしょう。

なお、「この曲がロードされていないときはロードする」といった条件判定の時に使える、と思われるかもしれませんが、LoadMusicメソッドは、同じ曲の2重登録を自動で防止しますので、特にそういった判定は必要ありません。

 

Musics [実行時のみ][読み出し専用]
property Musics[MusicIndex: integer]: String;

登録されている曲へのインデックスによるアクセスを提供します。Musics[0]が、一番最初に登録された曲名、Musics[MusicCount-1]が、最後に登録された曲名になります。MusicIndexに0未満の整数や、MusicCount以上の数字を指定した場合は、空の文字列を返します。ゲーム作成の場などでは滅多に使用することはないでしょう。


メソッド

Create
constructor Create(AOwner: TComponent); override;

いわゆるコンストラクタです。KbgmPlayerをフォームに貼り付けて使用するときは直接呼び出さないでください(Delphiによって自動的に生成されます)

 

Destroy
destructor Destroy; override;

デストラクタです。KbgmPlayerをフォームに貼り付けて使用するときは直接呼び出さないでください(Delphiによって自動的に破棄されます)

デストラクタでは、メモリ上の未解放の曲を全て破棄しますので、ユーザが、アプリケーションの終了前に明示的に曲データ(ハンドル)の解放を行う必要はありません。

 

LoadMusic
function LoadMusic(Music: String); boolean;

SMFファイルを手動でメモリ上に読み込み、登録します。Musicパラメータには、登録したいMIDIファイルの、拡張子.midを取り除いたものを渡してください。LoadMusicは、内部で、BaseDirectory + Music + '.mid'という名前のMIDIファイルを探して、メモリ上にロードします。

Musicパラメータは、そのまま、PlayやChangeToメソッドで使用するための「曲名」となります。

例えば、BaseDirectory = 'Midis\'のとき、LoadMusic('Title')とすれば、(exeファイルを基準とした相対パスで) 「Midis\Title.mid」という曲を探し出し、Titleという曲名で登録します。登録後は、その曲名を使って、Play('Title', false, 0, 0)といった感じで再生します。

また、一応LoadMusic('..\..\OtherFolder\Unrelated')とか、LoadMusic('Q:\Mukankei\faraway');といったような指定をすれば、遠く離れた曲を読み込むことも出来ますが、その場合は再生時に曲名として「..\..\OtherFolder\Unrelated」とパスも含めたものを指定してください(^^; 基本的にそのような使い方は想定していないので、よろしくお願いします。

なお、同じ曲名の2重登録はできません。逆にいえば、読み込まれているかどうか曖昧な場合は、わざわざチェックをしなくても、もう一度普通に登録しなおせば大丈夫です。

戻り値は、うまく登録できたときにtrue、登録できなかったときはfalseです。ThrowExceptionプロパティがtrueなら、読み込みできなかったときに同時に例外も生成されます。

このメソッドは、手動でSMFを読み込みますが、AutoLoadとAutoLoadMidisプロパティを使うことで、このメソッドを呼び出す必要は基本的にはなくなります。このメソッドを使うのは、メモリをわずかでも節約するためにこまめに曲をロード・アンロードしたいときに限ってください。1つのMIDIファイルは大きくても100KB程度でしょうから、相当たくさんのMIDIファイルを同時に読み込まない限り大丈夫だとは思いますが(画像ファイルと比べれば…)。

 

UnLoadMusic
procedure UnLoadMusic(Music: String);

ロードした曲を手動で解放します。Musicパラメータには、AutoLoadMidisに記入した曲名、もしくはLoadMusicのMusicパラメータに渡した値を使ってください。

アプリケーションの終了時などに明示的にこれを呼び出す必要はありません。未解放の曲は自動的に解放されます。したがって、このメソッドは、メモリを節約するために、使わない曲を手動で解放したいときにのみ使用してください。

すでに解放された曲名や、もとからロードされていない曲名を渡しても問題はありません。何もせずに処理を戻します。

 

Play
constructor Play(Music: String; IsRepeat: boolean; RepeatSeek: integer; FadeInTime: integer);

登録された曲の再生を開始します。Musicパラメータには、AutoLoadMidisに記入した曲名、もしくはLoadMusicのMusicパラメータに渡した値を使ってください。IsRepeatがtrueなら、繰り返し再生となります。そのときにRepeatSeek>0なら、最初の部分を飛ばした繰り返し再生となります。詳しくはKBGMのマニュアルを参照してください。FadeInTime>0なら、指定した秒数で音量100%になるようなフェードインで再生されます。

前の曲が再生中の場合は、強制的にその曲を停止しますが、前の曲がフェードアウト処理中の場合は、その曲を停止させることはできませんので、代わりにWaitingMusicに曲を登録します。前の曲のフェードアウトが終わり次第、指定した曲がなり始めます。

 

StopPlay
constructor StopPlay(FadeOutTime: integer);

登録された曲の再生を停止します。FadeOutTime>0なら、フェードアウト処理に入ります。

既にフェードアウト処理中の別の曲が再生中の場合、そのフェードアウト処理を中断することは出来ません。例えば、前に再生されていた曲が、StopPlay(10);で10秒間のフェードアウトに入った場合、その直後にStopPlay(0);を実行しても、曲は即座に停止せず、もとの10秒間のフェードアウトが継続されます。

フェードアウト処理中にStopPlayを呼び出したときには、WaitingMusicがクリアされるだけです。

 

ChangeTo
procedure ChangeTo(Music: String; FadeOutTime: integer;
                   IsRepeat: boolean; RepeatSeek: integer;
                   FadeInTime: integer);

曲を滑らかにつなぎます。具体的には、StopPlayとPlayを組み合わせたものになります。

名前はChangeToですが、曲が再生されていないときに呼び出せば、普通に再生が始まりますので、Playよりもこちらの方が使い勝手がいいでしょう。Playとの違いは、呼び出し前に曲が既に再生されていた場合、それをフェードアウトさせることが出来るかどうかです。

 

SetVolume
procedure SetVolume(Music: Stirng; Volume: integer);

KBGMSetVolumeを呼び出して、ボリュームを変更します。Volumeには0〜100の値をしていしてください。これは、再生を開始した後に呼び出してください。SetVolumeのあとにPlayすると、ボリュームはまた100に戻ります。

 

SetTempo
procedure SetTempo(Music: String; Tempo: integer);

KBGMSetTempoを呼び出して、テンポを変更します。

 


イベント

OnVolumeZero
property OnVolumeZero: TNotifyEvent;

KBGMによるフェードアウト処理が完了したときに呼び出されます。WaitingMusic<>''なら、このタイミングで次の曲の再生が開始されます。単に曲が切り替わるときに毎回呼び出されるわけではありません。また、SMFの中のマスターボリュームやボリュームコントロールがゼロになることに対応しているわけではありません。

曲のフェードアウトを始めて、完了したら場面切り替え、といった場合に使用するといいでしょう。

 

OnLoad
type
  TMidiLoadEvent = procedure(Sender: TObject; Music: String) of Object;

property OnLoad: TMidiLoadEvent;

曲が読み込まれたときに、曲ごとに発生します。Musicには、読み込まれた曲名が入っています。

AutoLoadMidisに入っている曲が自動で読み込まれたときと、LoadMusicで手動で読み込んだときに発生します。

 

OnUnLoad
type
  TMidiLoadEvent = procedure(Sender: TObject; Music: String) of Object;

property OnUnLoad: TMidiLoadEvent;

曲データがメモリ上から解放されたときに、曲ごとに発生します。Musicには、解放された曲名が入っています。

TKbgmPlayerがアプリケーションの終了時などに自動で曲を解放したときと、UnLoadMuiscで手動で解放したときに発生します。

 

OnAutoLoad
property OnAutoLoad: TNotifyEvent;

AutoLoadMidisプロパティにある曲が自動で読み込まれた後に、1度だけ発生します。

普通はフォームの生成時に発生しますが、Enabled=falseでアプリケーションが起動した後に、Enabled=trueになったときにも自動読み込みは発生します。

 

OnDestroy
property OnDestroy: TNotifyEvent;

TKbgmPlayerが破棄されるときに発生します。くどいようですが、個別の曲のメモリ上からの解放は自動で行われますので、ユーザがこのイベント上でやらないといけないことは特にありません。なんにでも使ってください。


謝辞

このコンポーネントの公開を快諾してくださったKr.Shinさん、本当にありがとうございました。

KBGM.dllはKr.Shinさんの著作物です。KBGMは、以下のURLから入手可能です。

名 称 KBGM Version 0.08
著作権 Copyright (c) 1996-1999 Kr.Shin
所在地 http://www.win.ne.jp/~krshin/ & NIFTY-Serve FGALGL LIB 5


使用に当たって


連絡先

バグ報告などは以下まで。

naruto
mailto:mikiso@gf6.so-net.ne.jp

CANO-Lab Home Page
http://www05.u-page.so-net.ne.jp/gf6/mikiso/cano-lab/

(C)2000 naruto/CANO-Lab