サウンドを再生する


別に、こんなことを知っていたからといって「だから何だ?」ってテクニックなんですが…(^^;

VBAから、WAVEファイルやMP3などのサウンドを再生するには次のようにします。

Shell関数を使う方法

Shell関数は、指定した外部プログラム(exeファイル)を起動する関数です。VBAの標準関数ですから、手軽に利用できるのがメリットです。

サウンドを再生するためにShell関数で起動するのは「Media Player」です。ただし、派手な視覚エフェクトが表示される最近のWindows Media Playerではなく、昔のWindowsから付属されていた古いMedia Playerを使います。

↑これが古いMedia Playerです。このMedia Playerは、再生したいサウンドファイルを指定することで、起動と同時に指定したサウンドファイルを再生することができます。さらに、再生が終了したら自動的にMedia Player自身を終了させるという引数もあります。

次のサンプルは、C:\Windows\Media\tada.wavを再生します。tada.wavは、Windowsを起動するときに鳴る例の「ジャジャ~ン」というサウンドファイルです。

Sub Sample1()
    Dim SoundFile As String
    SoundFile = "C:\Windows\Media\tada.wav"
    If Dir(SoundFile) = "" Then
        MsgBox SoundFile & vbCrLf & "がありません。", vbExclamation
        Exit Sub
    End If
    Shell "mplay32.exe /play /close " & SoundFile
End Sub

Media Playerの実体であるmplay32.exeは、通常Windows\System32フォルダにあります。一般的に、このフォルダにはパスが通っていますので、ファイル名だけを指定すれば起動できます。もちろん、mplay32.exeが存在しないとShell関数は失敗します。

上記のサンプルを実行して、もしサウンドが鳴りやまないときは、Media Playerのオプションで[繰り返し]がオンになっているのかもしれません。タスクバーなどでMedia Playerを開き、[編集]メニューの[オプション]をクリックして表示される[オプション]ダイアログボックスで、[繰り返す]チェックボックスをオフにしてください。

mplay32.exeでは、WAVEファイルだけでなく、MIDIファイル(*.mid)とMP3ファイル(*.mp3)も再生できました。

APIを使う方法

APIを使えば、Media Playerのような外部プログラムを使わずにサウンドを再生することも可能です。使うAPIはmciSendStringだけです。

次のサンプルは、APIを使ってC:\Windows\Media\tada.wavを再生します。

Private Declare Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" _
(ByVal lpstrCommand As String, ByVal lpstrReturnString As String, _
ByVal uReturnLength As Long, ByVal hwndCallback As Long) As Long
Sub Sample2()
    Dim SoundFile As String, rc As Long
    SoundFile = "C:\Windows\Media\tada.wav"
    If Dir(SoundFile) = "" Then
        MsgBox SoundFile & vbCrLf & "がありません。", vbExclamation
        Exit Sub
    End If
    rc = mciSendString("Play " & SoundFile, "", 0, 0)
End Sub

mciSendStringには他にもいろんな機能がありますが、解説しだすとExcel VBAの範疇ではなくなってしまいます。興味のある方は、googleなどでmciSendStringを検索してみてください。VisualBasic系のサイトなら参考になると思います。

追記:パスにスペースが含まれる場合

上記は、約10年前に書いたコンテンツです。日進月歩のIT業界、10年も経てば変化も大きいです。昔は可能だったテクニックが、現在は使用できない・・・なんてことも珍しくありません。上でご紹介した"mplay32.exe"は、Windows 7には含まれていないようです。Windows Vistaは分かりませんけど、たぶんないと思います。であれば、VBAからサウンドを再生するには、2番目にご紹介したAPIを使うのがいいでしょう。

ですが、実は上のAPIを使う方法で、ちょっとした悩みを持っていました。それは、

  • ファイル名またはパス名にスペースが含まれていると再生できない

ということです。たとえば、次のコードで「C:\SoundData\pingpong.wav」を再生できます。コードが煩雑になるので、ファイルの存在確認は割愛しました。また、mciSendStringの返り値を変数rcに入れて、イミディエイトウィンドウで確認します。0が返れば成功です。

Sub Sample3()
    Dim SoundFile As String, rc As Long
    SoundFile = "C:\SoundData\pingpong.wav"
    rc = mciSendString("Play " & SoundFile, "", 0, 0)
    Debug.Print rc
End Sub

このファイルなら再生できます。しかし、次のようにフォルダ名にスペースが含まれていると再生できません。

Sub Sample3()
    Dim SoundFile As String, rc As Long
    SoundFile = "C:\Sound Data\pingpong.wav"
    rc = mciSendString("Play " & SoundFile, "", 0, 0)
    Debug.Print rc
End Sub

この場合、mciSendStringの返り値は263でした。263がどんな意味の数値かは知りませんが、0ではないので、要するに失敗したってことです。

この手の「パス名やファイル名にスペースが入ったらダメよ」的なルールは、別に今回に限ったことではありません。たとえば、Windows Scripting HostのRunメソッドで何かを起動するときも、同じように、パス名やファイル名にスペースが含まれていると失敗します。しかし、その解決策も分かっています。パスを含むファイル名全体を、ダブルコーテーション("")で囲ってやればいいんです。今回のケースでも、きっとそれでOKのはずです。

念のためにググってみると、やはり「ダブルコーテーション("")で囲め」とありました。そこで、次のようにしてみました。

Sub Sample3()
    Dim SoundFile As String, rc As Long
    SoundFile = Chr(34) & "C:\Sound Data\pingpong.wav" & Chr(34)
    rc = mciSendString("Play " & SoundFile, "", 0, 0)
    Debug.Print rc
End Sub

ダブルコーテーション("")のアスキーコードは34なので、Chr関数で結合してみました。これは、

SoundFile = """" & "C:\Sound Data\pingpong.wav" & """"

SoundFile = """C:\Sound Data\pingpong.wav"""

と同じことです。なんか、""がたくさん出てきて意味分からない・・・って人は、次のページをご覧ください。

ダブルコーテーションの表示

さて、これで間違いないはずですし、ネットにもそう書いてあるのですが、結果はなぜか失敗です。mciSendStringの返り値も263になります。なにがいけないのでしょう・・・

そもそもmciSendStringは、VBAからサウンドを再生するためだけのAPIではありません。駆使することで、音楽プレイヤーを作成できるような、そんな高機能なAPIです。いろいろな機能を持っていて、音源ファイルを操作するときも、本来は「開いて(Open)」→「演奏して(Play)」→「閉じる(Close)」みたいな流れが一般的です。上記のように、OpenしていないファイルをいきなりPlayすると、便宜的に働いて、自動的にOpenし、終わったら自動的にCloseしてくれると。そうした便利機能を使っていたに過ぎません。

ネットでは、パスにスペースを含むファイルを再生する場合、ダブルコーテーション("")で囲めと書いてあります。それでうまくいったと書いてあります。しかし、よく見ると、そうした解説はどれも"Play"ではなく"Open"を使っています。今回、ダブルコーテーション("")で囲っても再生できなかったのは、いきなりPlayしていたのが原因かもしれません。そこで、次のようにやってみました。

Sub Sample3()
    Dim SoundFile As String, rc As Long
    SoundFile = Chr(34) & "C:\Sound Data\pingpong.wav" & Chr(34)
    rc = mciSendString("Open " & SoundFile, "", 0, 0)
    Debug.Print rc
    rc = mciSendString("Play " & SoundFile, "", 0, 0)
    Debug.Print rc
    rc = mciSendString("Close " & SoundFile, "", 0, 0)
    Debug.Print rc
End Sub

う~ん・・・これでも再生されません。あ!でもでも!イミディエイトウィンドウを見ると、いずれの返り値も0になっています。つまり、Open → Play → Closeは成功しているんです。成功しているのに、なぜ再生されないのだろう?はは~ん、さては各コマンドが非同期で実行されているんだな。つまり、Playした結果を待たずに、すぐ次の命令であるCloseが実行されてしまったと。だから、コマンド自体は成功しても、結果的に再生されなかったと。たぶん、そうだろうな。で、調べてみたら、同期モードでPlayさせるスイッチがありました。

Sub Sample3()
    Dim SoundFile As String, rc As Long
    SoundFile = Chr(34) & "C:\Sound Data\pingpong.wav" & Chr(34)
    rc = mciSendString("Open " & SoundFile, "", 0, 0)
    rc = mciSendString("Play " & SoundFile & " wait", "", 0, 0)
    rc = mciSendString("Close " & SoundFile, "", 0, 0)
End Sub

成功です。ちゃんと再生されました。

ちなみに、パス名やファイル名にスペースを含まない場合でも、ダブルコーテーション("")で囲ってかまいません。いちいち、パス名にスペースが含まれているかどうかを判定して処理を変える必要はありません。

また、ネットの情報では、パス名やファイル名に「スペースが含まれる」場合だけでなく、「日本語が含まれる」場合にもダブルコーテーション("")で囲めと書いてありましたが、試してみたところ、パス名やファイル名に日本語が含まれていても、それだけではダブルコーテーション("")で囲む必要はありませんでした。あ、試したのはWindows 7(64ビット版)です。