[BlueLeaf1336]> PROGRAM>

TWindowsMediaPlayer - 001

historyTOP

2004/06/20:作成

overviewTOP

普段動画を見る必要がある場合、A Player(Windows95/98/Me/画像&サウンド)を使用しています。いろいろ試したんですが、これが一番使い易いように思います。で、状況に応じて、Windows Media Player 9 シリーズや、Real.com - RealOnePlayer is now RealPlayerを使ったりもしています。もちろん後者の2つは異常に重たいですが。

それはそれとして、DelphiからTWindowsMediaPlayerを使いたくなりました。RealPlayerの方は置いといて、Windows Media Player 9 をDelphiから利用したいと、そういうことです。

基本方針としては、インターネット上のファイルではなく、ローカルに保存されているファイルを再生することだけを考えます。というか、実際に試してみた結果、ネットワーク上のファイルを扱うにはなにやらいろいろ邪魔くさそうだったのと、そんなことは本家に任せとけ、というあたりから、逆算された方針だったりします。

参考サイトTOP

次のリンクが、参考にしたサイトですが、ほとんどが「Windows Media Player 7 SDK」を元にしています。「9」ではなく「7」です。開発環境は「9」が入っているので取り返しがつかないのですが、「7」を使用している場合でも、そのままサンプルコードが使用できるはずです。「9にしかないメソッドやプロパティやイベントは一切使用していません」から。

ActiveXの取り込みTOP

まず、Windows Media Player をコンポーネントとして使うために、コンポーネントパレットに表示させる必要があります(と思う)。現在形は基本的な手順で、過去形になっているのはひょっとしたら変更すべきかも知れない手順です。

  1. Delphiのメニューで「ファイル(F)>全て閉じる(L)」をクリックします。
  2. Delphiのメニューで「コンポーネント(C)>ActiveXコントロールの取り込み(X)...」をクリックします。
  3. 「ActiveXの取り込み」という画面が開きます。
  4. 「Windows Media Player (Version 1.0)」を選択しました。
  5. 「インストール(I)...」をクリックします。
  6. ※一度インストールしているので「置き換えますか?」と聞かれました。「はい」で。
  7. 「インストール」という画面が開きます。ここはそのままで変更せず(「既存のパッケージに追加」というタブで、ファイル名が「...¥dclusr.dpk」)に「OK」をクリックしました。
  8. 「パッケージ - dclusr.dpk」という画面が開きました。
  9. 「インストール」ボタンをクリックしたいのに使用不可になっているので「コンパイル」をクリックしました。
  10. コンパイル後、「インストール」ボタンが使用可能になったのでクリックしました。
  11. ばつボタンで閉じようとすると、保存するかきかれたので、保存しました。
  12. 「ActiveX」タブに「TWindowsMediaPlayer」が追加されました。

というような感じで、だいたいでインストールします。いらなくなった時の消し方ですが、実はよく知りません。ただ、次のようにやるのが正しい削除のニアミスな方法だと思います。

  1. Delphiのメニューで「ファイル(F)>全て閉じる(L)」をクリックします。
  2. Delphiのメニューで「プロジェクト(P)>オプション(O)...」をクリックします。
  3. 「プロジェクトオプション」という画面が開きます。
  4. 「パッケージ」タブを選択し、「設計時パッケージ」一覧で「Borland User Components」のチェックをはずします。
  5. 「OK」をクリックします。
  6. 「ActiveX」タブから「TWindowsMediaPlayer」がなくなりました。

前提条件TOP

以降のサンプルでは、フォームに配置した「TWindowsMediaPlayer」の Name は「player」とします。

次のような関数を使用して、取得した値を表示します。

//  ---------------------------------------------------------------------------
//  デバッグ用メッセージ表示
procedure   TForm1.ShowMsg(const S: string);
begin
    if (Assigned(Memo1)) then
    begin
        if (S <> '') then Memo1.Lines.Add(S);
        if (Memo1.Lines.Count > 100) then Memo1.Lines.Delete(0);
    end;
end;

次のような配列を使用して、取得した値を表示します。

//  ---------------------------------------------------------------------------
//  ON/OFF表示用文字配列
const
    ON_OFF: array[Boolean] of string = ('OFF', 'ON');
    OK_NG: array[Boolean] of string = ('NG', 'OK');

プレイヤーの設定を調べるTOP

//  ---------------------------------------------------------------------------
//  プレイヤーの設定を調べる
procedure TForm1.Button14Click(Sender: TObject);

var
    b: Boolean;
    s: string;
    i: integer;
    d: double;
begin
    //  区切り線
    ShowMsg(StringOfChar('-', 20));

    //  バージョン
    s := player.versionInfo;
    ShowMsg(s);

    //  自動再生(変更可能)
    b := player.settings.autoStart;
    s := Format('自動再生 = %s', [ON_OFF[b]]);
    ShowMsg(s);

    //  バランス(変更可能)
    i := player.settings.balance;
    s := Format('ステレオバランス(-100..100) = %d', [i]);
    ShowMsg(s);

    //  エラーダイアログ(変更可能)
    b := player.settings.enableErrorDialogs;
    s := Format('エラーダイアログ表示 = %s', [ON_OFF[b]]);
    ShowMsg(s);

    //  ミュート(変更可能)
    b := player.settings.mute;
    s := Format('ミュート = %s', [ON_OFF[b]]);
    ShowMsg(s);

    //  再生回数(変更可能)
    i := player.settings.playCount;
    s := Format('再生回数 = %d', [i]);
    ShowMsg(s);

    //  再生速度(変更可能)
    d := player.settings.rate;
    s := Format('再生速度 = %f', [d]);
    ShowMsg(s);

    b := player.settings.isAvailable['Rate'];
    if (b) then
        s := '再生速度変更 = 可能'
    else
        s := '再生速度変更 = 不可';
    ShowMsg(s);

    //  音量(変更可能)
    i := player.settings.volume;
    s := Format('音量 = %d', [i]);
    ShowMsg(s);

    //  ループモード(変更可能)
    b := player.settings.getMode('loop');
    s := Format('ループ再生モード = %s', [ON_OFF[b]]);
    ShowMsg(s);

    //  シャッフルモード(変更可能)
    b := player.settings.getMode('shuffle');
    s := Format('ランダム再生モード = %s', [ON_OFF[b]]);
    ShowMsg(s);

    //  区切り線
    ShowMsg(StringOfChar('-', 20));
end;

プレイリストにメディアを追加するTOP

TWindowsMediaPlayerでは、再生対象のファイルはプレイリストで管理されています。プレイリストは、本家のプレイヤーでは、直接ファイルをドロップすると「再生リスト1」みたいな色気のない名前のプレイリストが作成されますが、Delphiで使う時も同様で、player.currentPlaylist で取り出せるプレイリストは表示を見る限り「再生リスト1」にあたるようです。多分保存したりすると話が変わるんでしょうけど。

で、問題は、プレイリストにファイルを追加する方法です。何故問題か、というと、プレイリストに追加するメソッドは、引数を、IWMPMedia という型で受けとるからで、これを単純にファイルパスから作り出す方法がよくわからないんです。

で、いろいろと検索したりSDKを眺めたりしていると、player.mediaCollection.add というメソッドが、IWMPMedia を返すことがわかりました。で、次のコードはこれを利用しているというわけです。

本来の player.mediaCollection は、メディアをぼこぼこと追加していき、著作権者とかジャンルとかで絞り込んだプレイリストを作成するメソッドがあるので、これを利用して大量のメディアから見たいメディアをビシッと取り出して、それだけを再生する、というような目的に使用するもののようです。

また、あとあとのために、プレイリストに何が入っているかを知っておく必要があるので、ListBoxに追加したメディアを表示しています。

//  ---------------------------------------------------------------------------
//  プレイリストにファイルを追加
procedure TForm1.Button17Click(Sender: TObject);
var
    i: integer;
    Media: IWMPMedia;
begin
    //  ファイルを選択させる
    if OpenDialog1.Execute then
    begin
        //  もっとも簡単な追加方法(ただし1ファイル限定)
        //player.URL := OpenDialog1.FileName;                
        //  全てのファイルについて
        for i := 0 to OpenDialog1.Files.Count - 1 do
        begin
            try
                //  プレイリストに追加するためのMediaオブジェクトを作成する
                Media := player.mediaCollection.add(OpenDialog1.Files[i]);
                //  プレイリストに追加する
                player.currentPlaylist.appendItem(Media);
                //  操作用のリストボックスにも追加する
                ListBox1.Items.Add(Media.name + '=' + Media.sourceURL);
            except
                //  未対応ファイルは無視する
                ;
            end;
        end;
    end;
end;

プレイリスト操作TOP

以下の2つの「選択したメディアを...する」という処理で、リストボックスとプレイリストの対応が必要になってきます。というかこのためにリストボックスに追加しています。

プレイリスト中のアクティブなメディアを変更するのも、メディアを削除するのも、相変わらず IWMPMedia で指定する必要があります。この IWMPMedia を探すのにまたしてもファイルパスは使用できないため(まあ普通のような気がしてきましたが)、リストボックスとの対応を取っているんですが、問題はSDKを見ると、「想定外のファイル削除にも対応しましょう」とあるんですね。つまりリストボックスには表示されているのに、実際のファイルがどこかに行方不明になっている状態がありうる、ということですよね。ここでは考えていません。

//  ---------------------------------------------------------------------------
//  選択したメディアを再生する
procedure TForm1.Button18Click(Sender: TObject);
begin
    if (ListBox1.ItemIndex = -1) then exit;
    //  念のため今のメディアを停止しておいて
    player.controls.stop;
    //  確認のために今のメディアの名称を表示
    Label3.Caption := player.currentMedia.name;
    //  メディアを切り替える
    player.controls.currentItem := player.currentPlaylist.Item[ListBox1.ItemIndex];
    //  確認のために今のメディアの名称を表示
    Label2.Caption := player.currentMedia.name;
    //  で、再生する
    player.controls.play;
end;

//  ---------------------------------------------------------------------------
//  選択したメディアを削除する
procedure TForm1.Button15Click(Sender: TObject);
var
    Media: IWMPMedia;
begin
    if (ListBox1.ItemIndex = -1) then exit;
    //  選択したメディアを取り出す
    Media := player.currentPlaylist.Item[ListBox1.ItemIndex];
    //  プレイリストからそのメディアを削除する
    player.currentPlaylist.removeItem(Media);
    //  リストボックスからも削除しておく
    ListBox1.DeleteSelected;
end;

以下の1行処理達は、実際のところ、TWindowsMediaPlayerをフォーム上に置いただけで、標準でコンポーネントに表示されているボタンにほぼ対応しています。つまり、本当にこれだけの処理を行うボタンを配置する必要は、コレッポッチもないだろうということです。

//  ---------------------------------------------------------------------------
//  再生リスト内の現在の項目の位置を、次の項目に
procedure TForm1.Button6Click(Sender: TObject);
begin
    player.controls.next();
end;
//  ---------------------------------------------------------------------------
//  再生リスト内の現在の項目の位置を、1 つ前の項目に
procedure TForm1.Button9Click(Sender: TObject);
begin
    player.controls.previous();
end;
//  ---------------------------------------------------------------------------
//  現在のメディア項目の再生を開始するか、または一時停止の項目の再生を再開
procedure TForm1.Button8Click(Sender: TObject);
begin
    player.controls.play();
end;
//  ---------------------------------------------------------------------------
//  メディア項目の再生を停止
procedure TForm1.Button10Click(Sender: TObject);
begin
    player.controls.stop();
end;
//  ---------------------------------------------------------------------------
//  メディア項目の再生を一時停止
procedure TForm1.Button7Click(Sender: TObject);
begin
    player.controls.pause();
end;
//  ---------------------------------------------------------------------------
//  メディア項目の早送りを開始
procedure TForm1.Button3Click(Sender: TObject);
begin
    player.controls.fastForward();
end;
//  ---------------------------------------------------------------------------
//  メディア項目の高速の巻き戻しを開始
procedure TForm1.Button4Click(Sender: TObject);
begin
    player.controls.fastReverse();
end;

時間に関する情報TOP

これは、再生中のメディアがないと意味がない情報です。実際、nilチェックを行わないままで、全くファイルを追加せずにこの処理を行うと、例外が発生してしまいます。

//  ---------------------------------------------------------------------------
//  再生中のメディアの時間に関する情報
procedure TForm1.Button1Click(Sender: TObject);
var
    s, s1, s2: string;
    d1, d2: double;
begin
    //  区切り線
    ShowMsg(StringOfChar('-', 20));

    //  再生中のメディアがあるかどうか
    if (player.currentMedia = nil) then
    begin
        s := '再生中のメディアがありません。';
        ShowMsg(s);
    end
    else
    begin
        //  現在のメディア項目の秒数で示した再生時間(時分秒)
        s1 := player.currentMedia.durationString;
        //  メディア項目内の現在位置取得(時分秒)
        s2 := player.controls.currentPositionString;
        //  連結
        s := Format('現在位置 %s / 再生時間 %s', [s2, s1]);
        ShowMsg(s);

        //  現在のメディア項目の秒数で示した再生時間
        d1 := player.currentMedia.duration;
        //  メディア項目内の現在位置取得(変更可能)
        d2 := player.controls.currentPosition;
        //  連結
        s := Format('現在位置 %f 秒 / 再生時間 %f 秒', [d2, d1]);
        ShowMsg(s);
    end;

    //  区切り線
    ShowMsg(StringOfChar('-', 20));
end;

次のものは、コンポーネントに標準でついているトラックバーで現在位置を変更した時などに呼ばれるイベント(OnPositionChange)です。元の位置と新しい位置が渡されてきます。どのように使用するのかわからないんですが、強制的に元に戻すとか?

//  ---------------------------------------------------------------------------
//  現在位置がマウスとかで変更された(イベント)
procedure TForm1.playerPositionChange(Sender: TObject; oldPosition,
  newPosition: Double);
var
    s: string;
begin
    //  区切り線
    ShowMsg(StringOfChar('-', 20));

    //  表示用に整形
    s := Format('%f 秒 -> %f 秒に変更', [oldPosition, newPosition]);
    ShowMsg(s);

    //  区切り線
    ShowMsg(StringOfChar('-', 20));
end;

to be continued...TOP

予想外に長くなってきたので、その2へ続きます。

EOFTOP