トップ回答者
DirectShow(GraphBuilder)でのタイムストレッチ

質問
-
お世話になっています。
厳密には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での動作を期待しています)
回答
-
Bで、例えば2倍速にすることを考えると、
方法1
SetRate(2.0) として、音程を下げるフィルタを通す。方法2
2.0 倍速く進むクロックを自作し、時間圧縮するフィルタを通す。以上のようなものがあると思います。
とはいえ DirectShow はご存知のとおり複雑なので、
実際やってみない事には断言できませんが。……ちなみに自分の方法では、
まず標準のレンダラの替わりになるオリジナルのレンダラを作り、
それに入ってくるサンプルを適当なバッファに一時保管しておきます。
んで、時間伸縮などの処理を行なう外部スレッドが、
そのバッファから読み出す、という仕組みです。
そして処理をしたあと、DirectSound などを使って、
自分でサウンド デバイスに流してやるわけです。
欠点としては、色々な段階で時間差を生ずることが挙げられます。
(特にシークが大変です)
すべての返信
-
Abstractさん、どうもありがとうございます。
>どうもそのようなメソッドが無いようなので
私も探しているのですが 無いかもしれないですね...
DirectSoundDeviceを再生に使用していますので、
教えていただいたIReferenceClockを、DirectSoundDeviceにQueryし
メソッドをいろいろと見てみたのですが期待しているメソッドは無いようでした。
となると、
>PCM に変換されたサンプルに対して、自分であれこれ計算処理をして時間伸縮を施しました。
とおっしゃられたように、
サウンドファイル -> PCMにデコード -> タイムストレッチ -> 再生
という処理を作成するべきなのかなと考えています。
しかしサウンドファイルサイズが大きくなればなるほど、
PCMへのデコード処理にかかるコストが大きくなりますし 悩みどころです。
またPCMデコードを行うとして 実装方法も悩んでいます。
この関係の処理に明るくないので、とんちんかんな事を書いているかも知れないのですが....
(そもそも実現不可能かも)
A.再生処理を実行する前にPCMへデコード
デコードしたWavデータ、メモリ上へタイムストレッチし、それを再生
B.タイムストレッチを行うフィルタを自作し、DirectSoundDeviceの直前に接続する
ソースフィルタ -> オーディオデコードフィルタ -> PCMフィルタ -> [タイムストレッチフィルタ] -> DirectSoundDevice
うーん... もう少し悩んでみます。
slicer
-
Bで、例えば2倍速にすることを考えると、
方法1
SetRate(2.0) として、音程を下げるフィルタを通す。方法2
2.0 倍速く進むクロックを自作し、時間圧縮するフィルタを通す。以上のようなものがあると思います。
とはいえ DirectShow はご存知のとおり複雑なので、
実際やってみない事には断言できませんが。……ちなみに自分の方法では、
まず標準のレンダラの替わりになるオリジナルのレンダラを作り、
それに入ってくるサンプルを適当なバッファに一時保管しておきます。
んで、時間伸縮などの処理を行なう外部スレッドが、
そのバッファから読み出す、という仕組みです。
そして処理をしたあと、DirectSound などを使って、
自分でサウンド デバイスに流してやるわけです。
欠点としては、色々な段階で時間差を生ずることが挙げられます。
(特にシークが大変です) -
お世話になります、Abstractさん。
あれから色々と調べてみたのですが、やはりBの方法(フィルタを自作)で考えてみようと思います。
そもそも、Aの方法では一時停止やシークなどの基本的な再生処理にも、手間がかかりそうですので...
実現可能かどうかは不明ですが以下の仕様のフィルタを作成してみようと考えていました
・入力ピン、出力ピンを持つ(CTransformFilter派生)
・入力ピンは、サウンドファイルのデコードフィルタのアウトプットピンから接続される
・タイムストレッチのパラメータを設定可能なメソッドを持つ
・入力されたデータは、タイムストレッチ処理(音程の変更+周波数の変更)を行い出力ピンへ
・出力ピンは、AudioRendererフィルタに接続する
...あくまで、机上で考えているので的外れなのかもしれませんが。
しかし助言していただいたように 方法1ならば、
再生速度の変更処理をSetRateが行ってくれていると考えることができますので、
作成するのは音程の変更処理のみというように、より簡潔にアプローチできそうです。
>まず標準のレンダラの替わりになるオリジナルのレンダラを作り、
(snip)>自分でサウンド デバイスに流してやるわけです
なるほど、GraphBuilder内で完結しない(で 宜しいですよね?)アプローチもあるのですね。
かならずAudioRendererフィルタまでたどり着かなければならないと思い込んでいましたので、
参考になります。
今回の処理の要求に照らし合わせて、もう少し勉強してみます。
slicer