- ベストアンサー
mp3再生中のISampleGrabberについて
DirectShowを使用してmp3を再生中に、 ISampleGrabberのGetCurrentBufferを使用すると、 現在再生中の位置の波形データを取得できると思うのですが。 毎回GetCurrentBufferを行っても約0.3秒間同じ値しか取得せず、 約0.3後に違う値が取得できる事が繰り返されます。 毎回現在の再生位置の波形データを取得するにはどうしたらよいのでしょうか。 ご存知の方いらっしゃいましたらお教えください。 宜しくお願い致します。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
音声再生は ・再生用バッファが2つ確保される ・バッファの片方に、コーデックに合わせてデコードされた生波形データが生成される ・生成が終ったバッファの生波形データが再生開始される ・再生中に、バッファのもう片方にも、コーデックに合わせてデコードされた生波形データが生成される ・片方のバッファがすべて再生されると、すぐに、もう片方のバッファが再生される ・再生が既に終った方のバッファは、上書きしても大丈夫なので、そこに続きのデータをデコードした生波形データが生成される ・上記のように「片方のバッファで再生しつつ、もう片方のバッファでデコードを行う」と言う作業が繰り返される と言う方法で再生されます。 GetCurrentBufferでは、上記のうち「2つのバッファのうち、今、再生に使ってる方」のデータのコピーが返されます。 片方のバッファサイズが「0.3秒分」であれば「どちらか片方のバッファが再生中の0.3秒間は、そのバッファのコピーが返され続ける」ので、0.3秒経たないと、続きのデータは取得出来ません。 因みに「取得した0.3秒間分のデータのうち、今の瞬間、どの位置を再生してるか?」は取得出来ません。
その他の回答 (3)
- machongola
- ベストアンサー率60% (434/720)
こんばんは。御礼頂きました。 その後、調査しているのですが、中々掴めていません。 改良してダイアログに表示してみたのですが、波形は入って来ている様です。 GetCurrentBuffer や BufferCB でバッファを取った時、バッファのサイズも渡されてきますが(私の場合は4608バイトでした)確かに同じデータが連続しています。 もしかしたら、サイズの分だけループさせる必要は無いのかもしれません。 バッファの先頭を処理するだけに留めたところ、一応其れらしき波形として表示されました。 ただ、現在再生位置よりも先のデータを処理している状態でした。 大分乱暴なプログラムで、16bitステレオしか処理していませんが、一応載せて置きます。試す場合は実験台の使い捨てプロジェクトの方で(コンソールの方です)。 #include<stdio.h> #include<dshow.h> #include<qedit.h> #include<deque>//STL #include"resource.h"//dialog resource #pragma comment(lib, "strmiids.lib") //CSampleGrabberCBのBufferCB又はSampleCBを決定する為のフラグ const int CBTYPE = 1; //波形を取るためのクラス //http://msdn.microsoft.com/ja-jp/library/cc369538.aspx struct CSampleGrabberCB : public ISampleGrabberCB { //ペン配列の使用用途 enum E_PenIndex { EPI_Wave = 0,//波形の色 EPI_Center = 1,//センターラインの色 EPI_Total = 2//配列個数 }; //ウェーブの音声をしまう構造 struct WaveSample { WaveSample(short __left, short __right) : left(__left), right(__right){ } ~WaveSample(){ } short left;//左 short right;//右 }; //LIFOバッファ typedef std::deque<WaveSample> QueWaveSample; //ダイアログプロシージャ static INT CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(uMsg == WM_CLOSE) { ::DestroyWindow(hDlg); ::PostQuitMessage(0); } return FALSE; } //コンストラクタ CSampleGrabberCB() : m_cRef(0), m_hDlg(NULL), m_hBmpBackBuffer(NULL) { //適当にペンを作成する const COLORREF aColors[EPI_Total] = {RGB(255, 128, 0), RGB(128, 255, 128)}; for(int i = 0; i < EPI_Total; ++i) m_arrhPen[i] = ::CreatePen(PS_SOLID, 1, aColors[i]); } //デストラクタ ~CSampleGrabberCB() { for(int i = 0; i < EPI_Total; ++i) ::DeleteObject(m_arrhPen[i]); ::DeleteObject(m_hBmpBackBuffer); } //ダイアログを開く HWND OpenDialog() { if(m_hDlg)return NULL; //ダイアログの作成 m_hDlg = ::CreateDialogParam(::GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc, 0); //クライアント領域に合わせてバックバッファを作成する RECT rc = {0}; ::GetClientRect(m_hDlg, &rc); m_hBmpBackBuffer = ::CreateBitmap(rc.right, rc.bottom, 1, 32, NULL); //ダイアログの表示 ::ShowWindow(m_hDlg, SW_SHOW); return m_hDlg; } private: //ココは今のところ使わない HRESULT WINAPI QueryInterface(REFIID refIID, LPVOID* ppUnk) { return S_OK; } //参照カウンタを上げる ULONG WINAPI AddRef() { return ++m_cRef; } //参照カウンタを下げる ULONG WINAPI Release() { if(--m_cRef)return m_cRef; //参照カウンタが0になったので自分を開放する delete this; //メンバに触ってはいけない return 0; } //CBTYPEが1の時、ココが呼ばれ続ける LONG WINAPI BufferCB(double SampleTime/*大体0.03秒単位だった*/, BYTE* pBuf, long bufLen/*此れを無視する*/) { //バックバッファのサイズを取る(ダイアログのクライアント領域でもある) const SIZE sz = this->GetBackBufferSize(); //16bitステレオ const short* pWaveData = (short*)pBuf; //波形描写の準備 HDC hDCBackBuffer = ::CreateCompatibleDC(NULL); ::SelectObject(hDCBackBuffer, m_hBmpBackBuffer); ::SelectObject(hDCBackBuffer, m_arrhPen[EPI_Wave]); ::PatBlt(hDCBackBuffer, 0, 0, sz.cx, sz.cy, BLACKNESS); //LIFOバッファのサイズがダイアログの幅より大きくなったら、先頭を捨てる if(m_queWaveSample.size() > sz.cx) m_queWaveSample.pop_front(); //ウェーブデータをLIFOバッファにプッシュする -32768 ~ +32767まで(画面に納めるために256で割って小さくする) //[0]が左の音声、[1]が右の音声 m_queWaveSample.push_back(WaveSample(pWaveData[0] / 256, pWaveData[1] / 256)); //LIFOバッファを走査してウェーブデータを描く int x = 0; for(QueWaveSample::iterator it = m_queWaveSample.begin(); it != m_queWaveSample.end(); ++it, ++x) { //画面の右~左へスクロールさせる(再生したての時しか意味はない) const int xPos = sz.cx - m_queWaveSample.size() + x; //波形を縦に書く const POINT aPt[] = {{xPos, (sz.cy / 2) - it->left}, {xPos, (sz.cy / 2) + it->right}}; ::Polyline(hDCBackBuffer, aPt, 2); } //センターラインを引く ::SelectObject(hDCBackBuffer, m_arrhPen[EPI_Center]); const POINT aPt[] = {{0, sz.cy / 2}, {sz.cx, sz.cy / 2}}; ::Polygon(hDCBackBuffer, aPt, 2); //バックバッファをダイアログにフリップする HDC hDCDialog = ::GetDC(m_hDlg); ::BitBlt(hDCDialog, 0, 0, sz.cx, sz.cy, hDCBackBuffer, 0, 0, SRCCOPY); ::ReleaseDC(m_hDlg, hDCDialog); ::DeleteDC(hDCBackBuffer); return S_OK; } //CBTYPEが0の時、ココが呼ばれ続ける HRESULT WINAPI SampleCB(double SampleTime, IMediaSample *pSample) { return S_OK; } //バックバッファの幅と高さを取る const SIZE GetBackBufferSize() const { BITMAP bitmap = {0}; ::GetObject(m_hBmpBackBuffer, sizeof(bitmap), &bitmap); const SIZE sz = {bitmap.bmWidth, bitmap.bmHeight}; return sz; } ULONG m_cRef;//参照カウンタ HWND m_hDlg;//ダイアログハンドル HBITMAP m_hBmpBackBuffer;//波形描写用のバックバッファ HPEN m_arrhPen[EPI_Total];//ペンの配列 QueWaveSample m_queWaveSample;//波形を溜め込むLIFOバッファ }; int main() { //必要なポインタ等 IGraphBuilder *pGraphBuilder = NULL; IMediaControl *pMediaControl = NULL; IBaseFilter *pSampleGrabberFilter = NULL; ISampleGrabber *pSampleGrabber = NULL; AM_MEDIA_TYPE am_media_type = {0}; //COMを初期化 CoInitialize(NULL); //FilterGraphを生成 CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC,IID_IGraphBuilder,(LPVOID *)&pGraphBuilder); //SampleGrabber(Filter)を生成 CoCreateInstance(CLSID_SampleGrabber,NULL,CLSCTX_INPROC,IID_IBaseFilter,(LPVOID *)&pSampleGrabberFilter); //FilterからISampleGrabberインターフェースを取得します pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (LPVOID *)&pSampleGrabber); //ウェーブに変換させる am_media_type.majortype = MEDIATYPE_Audio; am_media_type.subtype = MEDIASUBTYPE_PCM; am_media_type.formattype = FORMAT_WaveFormatEx; pSampleGrabber->SetMediaType(&am_media_type); //GraphにSampleGrabber Filterを追加 pGraphBuilder->AddFilter(pSampleGrabberFilter, L"Sample Grabber"); //コールバッククラスの設定 //http://msdn.microsoft.com/ja-jp/library/cc369545.aspx CSampleGrabberCB* pDialog = new CSampleGrabberCB(); pSampleGrabber->SetCallback(pDialog, CBTYPE); //MediaControlインターフェース取得 pGraphBuilder->QueryInterface(IID_IMediaControl,(LPVOID *)&pMediaControl); //mp3をロードする pMediaControl->RenderFile(L"test.mp3"); //メディアタイプの確認 //pSampleGrabber->GetConnectedMediaType(&am_media_type); //WAVEFORMATEX* wav = (WAVEFORMATEX*)am_media_type.pbFormat; //再生開始 pDialog->OpenDialog(); pMediaControl->Run(); //メッセージループ MSG msg; while(::GetMessage(&msg, NULL, 0, 0)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); } //再生を止める pMediaControl->Stop(); IMediaEvent *pEvent; pGraphBuilder->QueryInterface(IID_IMediaEvent,(LPVOID *)&pEvent); long evCode; pEvent->WaitForCompletion(INFINITE, &evCode); //解放 pSampleGrabber->SetCallback(NULL, CBTYPE); pSampleGrabber->Release(); pSampleGrabberFilter->Release(); pMediaControl->Release(); pGraphBuilder->Release(); //COM終了 CoUninitialize(); return msg.wParam;; }
お礼
返事が遅くなり申し訳ありません。 度々ありがとうございます。 取得する速度は若干早くなっておりますが、 やはり毎回取得するのは難しいようですね。 現在IGraphBuilder等を2つ作製し、 一つ目を先に波形データ作成用として無音で再生させ、バッファに貯めていきます。 出来た波形データ表示しながら少し遅れて二つ目を再生しようと考えています。 これで好きな場所の波形データを取得する事が出来ると思うのですが、 出来た波形データの現在再生中の位置を探し出す方法が考えつかず困っております。
- machongola
- ベストアンサー率60% (434/720)
こんにちは。 ISampleGrabberのGetCurrentBufferではなく、ISampleGrabberCBを継承して実装し、其のクラスをISampleGrabberに渡してあげれば良いのではないでしょうか。 取り敢えずコンソールなのですが、参考程度に。 #include <stdio.h> #include <dshow.h> #include <qedit.h> #pragma comment(lib, "strmiids.lib") //CSampleGrabberCBのBufferCB又はSampleCBを決定する為のフラグ const int CBTYPE = 1; //波形を取るためのクラス //http://msdn.microsoft.com/ja-jp/library/cc369538.aspx struct CSampleGrabberCB : public ISampleGrabberCB { //コンストラクタ CSampleGrabberCB() : m_cRef(0) { } //デストラクタ ~CSampleGrabberCB() { } private: //ココは今のところ使わない HRESULT WINAPI QueryInterface(REFIID refIID, LPVOID* ppUnk) { return S_OK; } //参照カウンタを上げる ULONG WINAPI AddRef() { return ++m_cRef; } //参照カウンタを下げる ULONG WINAPI Release() { if(--m_cRef)return m_cRef; //参照カウンタが0になったので自分を開放する delete this; //メンバに触ってはいけない return 0; } //CBTYPEが1の時、ココが呼ばれ続ける LONG WINAPI BufferCB(double SampleTime, BYTE* pBuf, long bufLen) { //取り敢えず表示して見る for(int i = 0, avg = 0; i < bufLen; ++i) { avg += pBuf[i]; } ::printf("[波形平均 %d][バッファ %d][サンプル時間 %f]\n", avg / bufLen, bufLen, SampleTime); return S_OK; } //CBTYPEが0の時、ココが呼ばれ続ける HRESULT WINAPI SampleCB(double SampleTime, IMediaSample *pSample) { return S_OK; } //参照カウンタ ULONG m_cRef; }; int main() { //必要なポインタ等 IGraphBuilder *pGraphBuilder = NULL; IMediaControl *pMediaControl = NULL; IBaseFilter *pSampleGrabberFilter = NULL; ISampleGrabber *pSampleGrabber = NULL; AM_MEDIA_TYPE am_media_type = {0}; //COMを初期化 CoInitialize(NULL); //FilterGraphを生成 CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC,IID_IGraphBuilder,(LPVOID *)&pGraphBuilder); //SampleGrabber(Filter)を生成 CoCreateInstance(CLSID_SampleGrabber,NULL,CLSCTX_INPROC,IID_IBaseFilter,(LPVOID *)&pSampleGrabberFilter); //FilterからISampleGrabberインターフェースを取得します pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (LPVOID *)&pSampleGrabber); //ウェーブに変換させる am_media_type.majortype = MEDIATYPE_Audio; am_media_type.subtype = MEDIASUBTYPE_PCM; am_media_type.formattype = FORMAT_WaveFormatEx; pSampleGrabber->SetMediaType(&am_media_type); //GraphにSampleGrabber Filterを追加 pGraphBuilder->AddFilter(pSampleGrabberFilter, L"Sample Grabber"); //コールバッククラスの設定 //http://msdn.microsoft.com/ja-jp/library/cc369545.aspx pSampleGrabber->SetCallback(new CSampleGrabberCB(), CBTYPE); //MediaControlインターフェース取得 pGraphBuilder->QueryInterface(IID_IMediaControl,(LPVOID *)&pMediaControl); //mp3をロードする pMediaControl->RenderFile(L"test.mp3"); //再生開始 pMediaControl->Run(); IMediaEvent *pEvent; pGraphBuilder->QueryInterface(IID_IMediaEvent,(LPVOID *)&pEvent); long evCode; pEvent->WaitForCompletion(INFINITE, &evCode); //解放 pSampleGrabber->SetCallback(NULL, CBTYPE); pSampleGrabber->Release(); pSampleGrabberFilter->Release(); pMediaControl->Release(); pGraphBuilder->Release(); //COM終了 CoUninitialize(); return 0; }
お礼
プログラムまで書いていただきありがとうございます。 早速組み込んでみたのですが結果は変わらず、 現在再生位置の波形データを取得する事はできませんでした。 取得したデータから現在再生中の位置を探し出すしかないのでしょうか。
- php504
- ベストアンサー率42% (926/2160)
bufferのサイズが0.3秒分であれば0.3秒間は同じデータが取得されて正常だと思います。
お礼
そうだったのですか、勉強不足でした。 ありがとうございます。
お礼
なるほど、音声再生はそのような方法で再生されていたのですね。 非常に分かりやすい説明をして頂き、ありがとうございます。 現在再生中の位置から少し先の波形データを取得して、 スペクトラムアナライザを表示しようと考えていたのですが、 再生中バッファの再生が終了しないと次のバッファが取得できないとの事なので、 少し難しい気がしてきました。