ホーム >プログラム >Delphi 6 ローテクTips >TWindowsMediaPlayerでギャップレス再生

TWindowsMediaPlayerを利用してギャップレス再生を行おうというページ。


プレイリストを利用

TWindowsMediaPlayerでギャップレス再生を行うにはプレイリストを使います。
やり方はとても簡単です。

 currentPlaylistにファイルを登録して再生。

それだけです。

procedure TForm1.Button1Click(Sender: TObject);
var
  l_Media: IWMPMedia;
begin
  if (OpenDialog1.Execute) then begin
    l_Media := IWMPPlayer4(WindowsMediaPlayer1.ControlInterface).newMedia(OpenDialog1.FileName);
    WindowsMediaPlayer1.currentPlaylist.appendItem(l_Media);
  end;
end;

プレイリストに登録すれば何もせずともギャップレスで連続再生してくれます。
モードを変更すればシャッフル再生もループ再生も行えます。
非常にお手軽です。

そのプレイリストにファイルを登録するにはappendItemメソッドを使います。
このappendItemメソッドは引数としてWideStringやAnsiStringなどのファイル名(文字列)ではなくIWMPMediaオブジェクトを要求します。
なのでファイル名からIWMPMediaオブジェクトをつくる必要があります。

ファイル名からIWMPMediaオブジェクトを得るには二つの方法があります。
一つは「BlueLeaf1336-PROGRAM-2004_0007」にあるようにmediaCollectionオブジェクトのaddメソッドの戻り値として得る方法です。
もう一つはnewMediaメソッドを使う方法です。
上の例では二つ目の方法でやっています。

一つ目のmediaCollection.addを使う方法は再生できないファイルを指定するとエラーが返ります。
エラーチェックをあらかじめやってくれるということで後々楽になります。
ただしちょっと時間がかかります。
またmediaCollectionオブジェクトというのは本家のプレーヤーであるWMP10(9や11とかでも)のライブラリそのものです。
なのでプログラム中からmediaCollectionオブジェクトにファイルを追加したり削除したりすると本家のプレーヤーのライブラリにも影響します。
またflvやdivxなどのファイルはffdshowやflvスプリッターなどをインストールしていてWMPで再生できる環境であってもエラーが返ってきてしまい、このやり方ではIWMPMeidaオブジェクトを取得できません。
flvやdivxファイルなどは、再生できる環境であってもメディアエラーが起きてしまうためです。
メディアエラーが起きてもリトライすることで再生させることは可能なのですがmediaCollectionオブジェクトにそのファイルを追加することはできません。
本来再生できるファイルなのに登録できないというのももったいないことです。

二つ目の方法はエラーチェックなしでIWMPMediaオブジェクトが返るので速いですしflvやdivxなどのファイルもプレイリストに登録可能です。
ただしチェックなしなので再生できないファイルであってもプレイリストに追加できてしまいます。
再生の時にエラーの対処がちょっと面倒になります。


再生させるには
プレイリスト中のメディアを再生させるにはplayメソッドではなくplayItemメソッドを使います。
playでも再生はできますがプレイリスト中の特定のメディアを指定することはできません。
で、playItemメソッドは引数にこれまたIWMPMediaオブジェクトを要求します。
procedure TForm1.Button1Click(Sender: TObject);
var
  l_Playlist: IWMPPlaylist;
  l_Media:    IWMPMedia;
begin
  if (OpenDialog1.Execute) then begin
    l_Playlist := WindowsMediaPlayer1.currentPlaylist;
    l_Media := IWMPPlayer4(WindowsMediaPlayer1.ControlInterface).newMedia(OpenDialog1.FileName);
    l_Playlist.appendItem(l_Media);
    WindowsMediaPlayer1.control.playItem(l_Playlist.Item[l_Playlist.count -1]);  //追加したファイルを再生
  end;
end;
このようにプレイリスト中のメディアオブジェクトを取り出して渡します。
ちなみに、
procedure TForm1.Button1Click(Sender: TObject);
var
  l_Media: IWMPMedia;
begin
  if (OpenDialog1.Execute) then begin
    l_Media := IWMPPlayer4(WindowsMediaPlayer1.ControlInterface).newMedia(OpenDialog1.FileName);
    WindowsMediaPlayer1.control.playItem(l_Media);
  end;
end;
のようにプレイリストに登録せずダイレクトに再生させようとしても「OLE エラー C00D1086.」というエラーが出てしまいます。
融通はききません。
flvやdivxファイルなどは先にも書いたように再生できる環境を整えていてもメディアエラーが起きます。
エラーが起きてもリトライすれば二度目からはエラーが起きることもなくちゃんと再生されます。
ただしTWindowsMediaPlayerはリトライせずすぐにプレイリスト中の次のアイテムを再生しようとします。
その結果例えば最初にflvファイルが三つあり、四つ目がwmaファイルだった場合、三つのflvファイルはエラーが起きるので再生されず、四つ目のwmaファイルが最初に再生されることになります。

1.flv、2.flv、3.flvの三つのファイルでエラーが起きます。

結局四つ目のtest.wmaが始めに再生されます。

再生の順番が重要になる場合は何らかの対策を施さないとなりません。
ただ、エラーが起きた場合WindowsMediaPlayerが自動で(あるいは勝手に)次のアイテムの再生を始めてしまうのでその制御を行わないといけないところがやっかいです。
flvやdivxなどは、一度エラーを起こしてしまえばプレイリストから削除されない限り次からはエラーを起こすことなくちゃんと再生されるようになります。
プレイリストにあるのがflvやdivxばかりのファイルだと次々とエラーが起こり最後までエラーのままで終了し、そのままでは結局再生は始まりません。
手動で再生ボタンを押すかOnPlayStateChangeイベントでNewStateがwmppsReadyになるのを捕まえてcontrols.playItemで再生を始めるようにする必要があります。
ListBoxやComboBoxとの同期
ただ垂れ流し的にギャップレス再生するのならこれだけでOKです。
でも、せっかくのプレイリストなのでListBoxやComboBoxのリストと同期して表示させたいところです。
procedure TForm1.Button1Click(Sender: TObject);
var
  l_Playlist: IWMPPlaylist;
  l_Media:    IWMPMedia;
  i: Integer;
begin
  if (OpenDialog1.Execute) then begin
    l_Playlist := WindowsMediaPlayer1.currentPlaylist;
    l_Playlist.Clear;
    ComboBox1.Items.Clear;
    for i := 0 to OpenDialog1.Files.Count -1 do begin
      l_Media := IWMPPlayer4(WindowsMediaPlayer1.ControlInterface).newMedia(OpenDialog1.Files[i]);
      l_Playlist.appendItem(l_Media);
      ComboBox1.Items.Add(l_Media.sourceURL);
    end;
    WindowsMediaPlayer1.controls.playItem(l_Playlist.Item[0]);
  end;
end;
プレイリスト中のメディアを取り出すのはcurrntPlaylist.Item[i]のようにItemプロパティに添え字をつけます。
それは手軽にできるのですが、その逆の今再生しているのはプレイリスト中の何番目のメディアかを調べるのは手軽ではありません。
それは現在再生しているメディアがプレイリスト中の何番目に位置するのかを表す、ListBoxやComboBoxでいうところのItemIndexに相当するプロパティやメソッドがないからです。
結局プレイリスト中のインデックスを知るにはメディアオブジェクトのIsIdenticalメソッドを使ってcurrentPlaylist中のアイテムと照合して判断するしかありません。
procedure TForm1.WindowsMediaPlayer1PlayStateChange(Sender: TObject; NewState: Integer);
var
  i: Integer;
begin
  if (NewState = wmppsPlaying) then begin
    for i := 0 to WindowsMediaPlayer1.currentPlaylist.count -1 do begin
      if (WindowsMediaPlayer1.currentMedia.isIdentical[WindowsMediaPlayer1.currentPlaylist.Item[i]]) then begin
        ComboBox1.ItemIndex := i;
        Break;
      end;
    end;
  end;
end;
ちなみに
  if (WindowsMediaPlayer1.currentMedia = WindowsMediaPlayer1.currentPlaylist.Item[i]) then begin
  if (WindowsMediaPlayer1.controls.currentItem = WindowsMediaPlayer1.currentPlaylist.Item[i]) then begin
などとしてもだめでした。
制限
ギャップレス再生できるのは音声ファイルのみのようです。
ビデオファイルは若干間が空きます。
またビデオファイルが続く場合、最初のビデオファイルはハードウェアオーバーレイを使えますが、それに続くビデオファイルはオーバーレイを使えません。
これは始めのビデオが終わりに近づいた時点(だけどまだ再生途中)で次のビデオファイルを開いて待機させるような動作になっているためです。
次のビデオを開いた時点では始めのビデオがハードウェアオーバーレイを使っているので次のビデオはハードウェアオーバーレイを使うことができないということです。
それで困ることといえば映像が乱れてしまうことがあることでしょうか。

ハードウェアオーバーレイを使えなかった場合。
このようにビデオウィンドウに他のウィンドウが重なると映像が乱れることがあります。

このような現象を避けるにはビデオアクセラレータの設定を変える方法があります。

WMPのメニューで「ツール」→「オプション」と進みます。

「パフォーマンス」タブの「ビデオ アクセラレータ」グループにある「詳細」ボタンを押します。

「ビデオ アクセラレータ」グループの上の項目の「ビデオ ミキシング レンダラを使う」で「高画質モードを使う」にチェックを入れます。
「オーバーレイを使う」のチェックは外します。

これでハードウェアオーバーレイを使っていなくてもビデオウィンドウに他のウィンドウが重なっても映像が乱れることはなくなります。
ただこの方法にも弊害があって、それはCPUの使用率が上がってしまうことです。
CPUに余裕があればどうということもないのでしょうが非力なCPUの場合はつらいところです。
最近のPCではハードウェアオーバーレイが複数使えるようです。
2009年に買ったネットブックでは同時に二つのハードウェアオーバーレイが使えています。

2009-03-10