■ エクセルでも音が出せる?
ゲームプログラミングにおいて効果音やBGMの使用はもはや必須ですが、業務アプリケーションにおいてもサウンドの使用は様々な利点があります。
そのもっとも効果的な使用法は、内部処理の進捗やイベントを「音」で知らせることでしょう。
たとえば長時間のバッチ処理の終了を音で知らせたり、タスクバーに常駐するソフトがイベントの発生(メールが届いた、エラーが発生した、等)を音で知らせる場合です。
マルチタスクが当たり前になった現在のWindowOSの様々な局面で「音」の使用はユーザーの利便性を高めます。
エクセルVBAでも音が出せるのでしょうか?
大丈夫です。API関数からDirectXを使用したものまで、さまざまな効果音・BGMの再生方法が可能です。
■ PlaySound関数で音を鳴らしてみよう!
最も簡単にWAVEファイルの再生を行う方法がPlaySoundです。
ところでこのPlaySoundはExcelVBAの機能ではありません。Windowsのもつwinmm.dllというライブラリの中にあるAPI関数の名前です。APIはApplication Programming Interfaceの略で、VBAだけでなく様々な開発言語から呼び出せるOSを制御するためにWindowsが用意した標準のインターフェイスです。
' モジュール宣言部に記述ここから −−−−−−−−−−−−−−−−−−−−−−−−−−
Public Declare Function PlaySound Lib "winmm.dll" Alias "PlaySoundA" ( _
ByVal pszSound As String, _
ByVal hmod As Long, _
ByVal fdwSound As Long _
) As Long
Public Const SND_ASYNC = &H1
' モジュール宣言部に記述ここまで −−−−−−−−−−−−−−−−−−−−−−−−−−
Public Sub test()
Dim WrkSndFile As String
WrkSndFile = ThisWorkbook.Path & "\test.wav"
PlaySound WrkSndFile, 0, SND_ASYNC
End Sub
これで実行ブックと同一フォルダにあるtest.wavが再生できます。どうやって再生しているのか説明していきましょう。
Declare Function〜 DLL内にある関数を呼び出すことを宣言しています。PlaySound Lib "winmm.dll" は使用するPlaySound関数がwinmm.dllライブラリ内にあることを明示しています。
引数と定数の定義はこちらを参照してください。
http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/jpmltimd/html/_win32_playsound.asp
testプロシージャを実行すると、まずFileName変数に再生するWaveファイルのフルパスが格納されます。次に
PlaySound FileName, 0, SND_ASYNC でWaveファイルを呼び出し非同期再生を行っています。
SND_ASYNC定数が非同期再生の指定になります。
Public Const SND_ASYNC = &H1 の部分を
Public Const SND_SYNC = &H0 とSND_SYNC定数を定義、
PlaySound WrkSndFile, 0, SND_SYNC と指定してやることで同期再生になります。
同期・非同期の違いですが、非同期再生の場合Waveファイルの再生中でも他の操作を受け付けます。同期再生を指定するとWaveファイルの再生が終わるまでマクロ、その他の動作を一切受け付けなくなります。
通常は上記のやり方で十分なんですが、ゲームなどで頻繁に効果音を再生する場合、ファイルからの読み取りでは対応しきれない場合があります。このときプログラムが一時的に停止したり、音再生が出遅れたりします。
少し改良してメモリ上のバッファから再生できるようにしてみましょう。
' モジュール宣言部に記述ここから −−−−−−−−−−−−−−−−−−−−−−−−−−
Public Declare Function PlaySound Lib "winmm.dll" Alias "PlaySoundA" ( _
ByRef pszSound As Byte, _
ByVal hmod As Long, _
ByVal fdwSound As Long _
) As Long
Public Const SND_ASYNC = &H1
Public Const SND_MEMORY = &H4
Public BufSndTest() As Byte
' モジュール宣言部に記述ここまで −−−−−−−−−−−−−−−−−−−−−−−−−−
Public Function ReadSoundBuffer()
Dim WrkSndFile As String
Dim WrkNumber As Long
WrkSndFile = ThisWorkbook.Path & "\test.wav"
WrkNumber = FreeFile()
Open WrkSndFile For Binary As WrkNumber
ReDim BufSndTest(LOF(WrkNumber))
Get WrkNumber, , BufSndTest
Close WrkNumber
End Function
Public Sub test()
Call ReadSoundBuffer
PlaySound BufSndTest(0), 0, SND_ASYNC + SND_MEMORY
End Sub
通常はReadSoundBufferの処理は、ブックもしくはフォームを開いた時点で一度実行しておけばOKです。
BufSndTest 動的配列にtest.wavの中身がバイナリで格納されます。pszSound 第一引数がByte型の参照渡しに変更されていることに注目してください。
第一引数には再生するWaveファイルのバイト列を渡してやる必要があります。値渡しではせっかくメモリに格納したバイナリを別領域に値として格納しなおしてしまいます。
変数(BufSndTest)に格納されたバイト配列の、あくまでポインタを渡すために参照渡しを指定しています。
ReadSoundBufferの処理ですが、test.wavをバイナリとして読み込んだあとLOF関数でサイズを調べ、BufSndTestの配列数を再定義しています。あとは、定義しなおしたByte型配列の中にバイナリを格納していきます。
このBufSndTestはグローバルで定義していますので、プログラムのあらゆるところから呼び出しが可能です。
PlaySound BufSndTest(0), 0, SND_ASYNC + SND_MEMORY BufSndTest配列の先頭ポインタを渡しています。fdwSound(再生フラグ)は非同期(SND_ASYNC)にメモリ再生(SND_MEMORY)のフラグが追加されています。
このPlaySound関数は大変扱いやすく、もっとも簡単にサウンド機能を提供してくれるのですが、いくつか制限もあります。
たとえば、サウンドの停止にはpszSound引数にNullを指定します。(pszSoundがByte型の場合は0)
これは常に再生の停止を意味し、いわゆる一時停止はできません。(次回再生時は常にファイルの先頭からになります)
さらに一度に鳴らすことのできるWaveファイルは一つまでで、同時に複数のWaveファイルを鳴らすことができません。
(再生の途中で他の音を鳴らそうとすると先に鳴っていたWaveは中断されます)
これらの不便を一気に解消してくれるのがDirectXによる再生法です。
しかもPlaySoundでは鳴らせなかったMidiやMP3ファイルの再生も可能です。
■ DirectXでWaveファイルを再生してみよう!
さあ、DirectXで音を鳴らしてみましょう。DirectXにはバージョンがありますが今回はDirectXの8を使って説明したいと思います。まずVBEのツール>参照設定で
"DirectX 8 for Visual Basic Type Library"にチェックを入れてください。
これで準備OKです。それでは先ほどのコードを早速書き換えてみましょう。
' モジュール宣言部に記述ここから −−−−−−−−−−−−−−−−−−−−−−−−−−
Public Declare Function FindWindow Lib "User32.dll" Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String _
) As Long
Dim DX8 As New DirectX8
Dim DS8 As DirectSound8
Dim BufSndTest As DirectSoundSecondaryBuffer8
Dim BufDsc As DSBUFFERDESC
Dim MyWindow As Long
Dim WrkSndFile As String
' モジュール宣言部に記述ここまで −−−−−−−−−−−−−−−−−−−−−−−−−−
Public Sub ReadSoundBuffer()
MyWindow = FindWindow("XLMAIN", Application.Caption)
WrkSndFile = ThisWorkbook.Path & "\test.wav"
Set DS8 = DX8.DirectSoundCreate(vbNullString)
DS8.SetCooperativeLevel Hwnd:=MyWindow, Level:=DSSCL_PRIORITY
Set BufSndTest = DS8.CreateSoundBufferFromFile( _
Filename:=WrkSndFile, _
bufferDesc:=BufDsc _
)
End Sub
Public Sub test()
Call ReadSoundBuffer
BufSndTest.Play DSBPLAY_DEFAULT
Do
DoEvents
Loop Until BufSndTest.GetStatus = 0
End Sub
こんな感じになります。まず最初のAPI関数、FindWindowですがExcelのウィンドウハンドルを取得するために必要です。DirectX(正確にはDirectSound8オブジェクト)は再生する前に必ずアプリケーション協調レベルを設定しなければなりません。このとき、Excelのハンドルを渡す必要があるのでFindWindowが必要になるわけです。
Dim DX8 As New DirectX8 まずDirectX8の新しいオブジェクトを宣言します。
次にDirectXの主要なコンポーネントオブジェクトであるDirectSound8のオブジェクトを作成します。
このときは DirectX8.DirectSoundCreate の戻り値を代入します。
Dim DS8 As DirectSound8
Set DS8 = DX8.DirectSoundCreate(vbNullString)
さて、作成したDS8オブジェクトを初期化します。このときExcelのハンドルと協調レベルDSSCL_PRIORITY(優先的に使用する)を設定します。
Set BufSndTest = ここでサウンドバッファにWaveファイルを読み込みます。CreateSoundBufferFromFileというメソッドがDirectSound8には用意されています。ここで指定したファイルWrkSndFileを作成されたバッファにロードしています。bufferDescに指定されているBufDscはDSBUFFERDESC構造体でWaveフォーマットやエフェクトなどの指定を行うことが可能です。
こうしてBufSndTest内にサウンドバッファがセットされました。
音の再生はtestプロシージャからおこなっている、
BufSndTest.Play DSBPLAY_DEFAULT の部分です。DSBPLAY_DEFAULT はバッファの再生方法の指定で、再生カーソルの位置からバッファの最後までを再生する指定になっています。
Do〜LoopでBufSndTest.GetStatus = 0 の間ループさせているのはプロシージャが終了してしまうと再生が終了してしまうからです。GetStatusが0 のとき再生が終了していますので、それまで待ってプロシージャを終了させるようにしています。
次回は、DirectXを使用したMIDIサウンドの再生とWAVE、MIDIを組み合わせた多重再生についてご紹介したいと思います。
|