none
DirectShow(GraphBuilder)でのタイムストレッチ RRS feed

  • 質問

  • お世話になっています。

    厳密にはC++ではなく DirectX関連になるのですが、ここに質問させていただきました。

     

    現在 VC2005上で、IGraphBuilderを使用して音楽、音声ファイルを再生するプロジェクトを作成しています。

    再生可能となる音楽ファイルは、パソコンにインストールされているフィルタに依存するものとしています。

    また 動画の再生は考慮していません。

     

    実際にプログラミングし、

    再生、シークバー、早送り、音量、音量バランスなどの処理を実装し、期待通りの動作をしています。

     

    再生処理は以下の手順で行っています

       CComPtr<IGraphBuilder>   m_GraphBuilder;         // IGraphBuilderをメンバクラスとしています

                ...

       m_GraphBuilder.CoCreateInstance( CLSID_FilterGraph );

       m_GraphBuilder.AddFilter( CLSID_DSoundRender,  xxx );   // DirectSoundDeviceを登録

       m_GraphBuilder->RenderFile( 再生したいファイル, NULL) );  // インテリジェント接続に全て任せています

       m_GraphBuilder-.Run( );

     

    ここで本題なのですが、再生時に早送り再生のような処理を実装したいのです。

    俗に言うタイムストレッチというものでしょうか?

     

    IGraphBuilder からIMediaSeekingインターフェースをQueryし、

    IMediaSeeking->SetRateメソッドを使用すれば早送りにはなるのですが、

    再生音程もあがってしまうので好ましくありません。

     

    あくまでも 再生速度は早くなるが、音程はそのまま というのを目指しています。

     

    何かよい手段はありませんでしょうか?

    再生周波数を変更する方法や、再生音程を下げる方法などでもありがたいです。

     

    よろしくお願いいたします。

     

    slicer

     

    環境

    ・Windows Vista (XPも考慮します)

    ・VisualStudio2005  C++

    ・Microsoft DirectX SDK (March 2008)

    ・DirectX 10 (開発環境はDirectX 10ですが、できるだけ古いバージョンのDirectXでの動作を期待しています)

    2008年8月5日 13:42

回答

  • Bで、例えば2倍速にすることを考えると、

     

    方法1
    SetRate(2.0) として、音程を下げるフィルタを通す。

     

    方法2
    2.0 倍速く進むクロックを自作し、時間圧縮するフィルタを通す。

     

    以上のようなものがあると思います。
    とはいえ DirectShow はご存知のとおり複雑なので、
    実際やってみない事には断言できませんが。……

     

    ちなみに自分の方法では、
    まず標準のレンダラの替わりになるオリジナルのレンダラを作り、
    それに入ってくるサンプルを適当なバッファに一時保管しておきます。
    んで、時間伸縮などの処理を行なう外部スレッドが、
    そのバッファから読み出す、という仕組みです。
    そして処理をしたあと、DirectSound などを使って、
    自分でサウンド デバイスに流してやるわけです。
    欠点としては、色々な段階で時間差を生ずることが挙げられます。
    (特にシークが大変です)

    2008年8月6日 14:00

すべての返信

  • DX8 で作ったことがありますが、
    どうもそのようなメソッドが無いようなので、
    仕方ないから PCM に変換されたサンプルに対して、
    自分であれこれ計算処理をして時間伸縮を施しました。
    (具体的方法は、長くなるので割愛します)

     

    今は、どうでしょうねぇ。探せばメソッドがあるかも知れません。
    標準のでなくても、誰かが公開しているかも知れないし。
    あればいいですね。Smile

    2008年8月5日 23:26
  • 先ほど書いたことは、他の機能との兼ね合いもあったためで、
    再生速度を変更するだけなら、
    もしかすると IReferenceClock でできるかも知れません。
    (未確認。できないかも知れません。)

    2008年8月5日 23:43
  • Abstractさん、どうもありがとうございます。

     

    >どうもそのようなメソッドが無いようなので

    私も探しているのですが 無いかもしれないですね...

     

    DirectSoundDeviceを再生に使用していますので、

    教えていただいたIReferenceClockを、DirectSoundDeviceにQueryし

    メソッドをいろいろと見てみたのですが期待しているメソッドは無いようでした。

     

    となると、

    >PCM に変換されたサンプルに対して、自分であれこれ計算処理をして時間伸縮を施しました。

    とおっしゃられたように、

     サウンドファイル -> PCMにデコード -> タイムストレッチ -> 再生

    という処理を作成するべきなのかなと考えています。

     

    しかしサウンドファイルサイズが大きくなればなるほど、

    PCMへのデコード処理にかかるコストが大きくなりますし 悩みどころです。

     

    またPCMデコードを行うとして 実装方法も悩んでいます。

    この関係の処理に明るくないので、とんちんかんな事を書いているかも知れないのですが....

    (そもそも実現不可能かも)

     

     A.再生処理を実行する前にPCMへデコード

       デコードしたWavデータ、メモリ上へタイムストレッチし、それを再生

     B.タイムストレッチを行うフィルタを自作し、DirectSoundDeviceの直前に接続する

    ソースフィルタ -> オーディオデコードフィルタ -> PCMフィルタ -> [タイムストレッチフィルタ] -> DirectSoundDevice

     

    うーん... もう少し悩んでみます。

     

    slicer

    2008年8月6日 8:56
  • Bで、例えば2倍速にすることを考えると、

     

    方法1
    SetRate(2.0) として、音程を下げるフィルタを通す。

     

    方法2
    2.0 倍速く進むクロックを自作し、時間圧縮するフィルタを通す。

     

    以上のようなものがあると思います。
    とはいえ DirectShow はご存知のとおり複雑なので、
    実際やってみない事には断言できませんが。……

     

    ちなみに自分の方法では、
    まず標準のレンダラの替わりになるオリジナルのレンダラを作り、
    それに入ってくるサンプルを適当なバッファに一時保管しておきます。
    んで、時間伸縮などの処理を行なう外部スレッドが、
    そのバッファから読み出す、という仕組みです。
    そして処理をしたあと、DirectSound などを使って、
    自分でサウンド デバイスに流してやるわけです。
    欠点としては、色々な段階で時間差を生ずることが挙げられます。
    (特にシークが大変です)

    2008年8月6日 14:00
  • お世話になります、Abstractさん。

     

    あれから色々と調べてみたのですが、やはりBの方法(フィルタを自作)で考えてみようと思います。

    そもそも、Aの方法では一時停止やシークなどの基本的な再生処理にも、手間がかかりそうですので...

     

    実現可能かどうかは不明ですが以下の仕様のフィルタを作成してみようと考えていました

     ・入力ピン、出力ピンを持つ(CTransformFilter派生)

     ・入力ピンは、サウンドファイルのデコードフィルタのアウトプットピンから接続される

     ・タイムストレッチのパラメータを設定可能なメソッドを持つ

     ・入力されたデータは、タイムストレッチ処理(音程の変更+周波数の変更)を行い出力ピンへ

     ・出力ピンは、AudioRendererフィルタに接続する

    ...あくまで、机上で考えているので的外れなのかもしれませんが。

     

    しかし助言していただいたように 方法1ならば、

    再生速度の変更処理をSetRateが行ってくれていると考えることができますので、

    作成するのは音程の変更処理のみというように、より簡潔にアプローチできそうです。

     

     

    >まず標準のレンダラの替わりになるオリジナルのレンダラを作り、
    (snip)

    >自分でサウンド デバイスに流してやるわけです


    なるほど、

    GraphBuilder内で完結しない(で 宜しいですよね?)アプローチもあるのですね。

    かならずAudioRendererフィルタまでたどり着かなければならないと思い込んでいましたので、

    参考になります。

     

    今回の処理の要求に照らし合わせて、もう少し勉強してみます。

     

    slicer

    2008年8月6日 15:35
  • こんにちは。中川俊輔 です。

     

    Abstractさん、回答ありがとうございます。

     

    slicerさん、フォーラムのご利用ありがとうございます。

    有用な情報と思われたため、Abstractさんの回答へ回答済みチェックをつけさせていただきました。

     

    回答済みチェックが付くことにより、有用な情報を探している方が情報を見つけやすくなります。
    有用な情報と思われる回答があった場合は、なるべく回答済みボタンを押してチェックを付けてください。

    slicerさんはチェックを解除することもできますので、ご確認ください。

     

    それでは!

    2008年8月21日 8:21
  • こんにちは、中川俊輔さん。

    申し訳ありません、回答済みチェックを忘れていました。
    今後は気をつけるようにいたします。

    どうもありがとうございました。

     

    Abstractさん、どうもありがとうございました。

    改めてお礼申し上げます。

     

    slicer

    2008年9月19日 5:56