プロが教える店舗&オフィスのセキュリティ対策術

DirectShowを使用してmp3を再生中に、
ISampleGrabberのGetCurrentBufferを使用すると、
現在再生中の位置の波形データを取得できると思うのですが。
毎回GetCurrentBufferを行っても約0.3秒間同じ値しか取得せず、
約0.3後に違う値が取得できる事が繰り返されます。

毎回現在の再生位置の波形データを取得するにはどうしたらよいのでしょうか。
ご存知の方いらっしゃいましたらお教えください。
宜しくお願い致します。

A 回答 (4件)

音声再生は


・再生用バッファが2つ確保される
・バッファの片方に、コーデックに合わせてデコードされた生波形データが生成される
・生成が終ったバッファの生波形データが再生開始される
・再生中に、バッファのもう片方にも、コーデックに合わせてデコードされた生波形データが生成される
・片方のバッファがすべて再生されると、すぐに、もう片方のバッファが再生される
・再生が既に終った方のバッファは、上書きしても大丈夫なので、そこに続きのデータをデコードした生波形データが生成される
・上記のように「片方のバッファで再生しつつ、もう片方のバッファでデコードを行う」と言う作業が繰り返される
と言う方法で再生されます。

GetCurrentBufferでは、上記のうち「2つのバッファのうち、今、再生に使ってる方」のデータのコピーが返されます。

片方のバッファサイズが「0.3秒分」であれば「どちらか片方のバッファが再生中の0.3秒間は、そのバッファのコピーが返され続ける」ので、0.3秒経たないと、続きのデータは取得出来ません。

因みに「取得した0.3秒間分のデータのうち、今の瞬間、どの位置を再生してるか?」は取得出来ません。
    • good
    • 0
この回答へのお礼

なるほど、音声再生はそのような方法で再生されていたのですね。
非常に分かりやすい説明をして頂き、ありがとうございます。

現在再生中の位置から少し先の波形データを取得して、
スペクトラムアナライザを表示しようと考えていたのですが、
再生中バッファの再生が終了しないと次のバッファが取得できないとの事なので、
少し難しい気がしてきました。

お礼日時:2009/02/08 00:24

 こんばんは。

御礼頂きました。

 その後、調査しているのですが、中々掴めていません。
 改良してダイアログに表示してみたのですが、波形は入って来ている様です。
 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 …
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;
}

ULONGm_cRef;//参照カウンタ
HWNDm_hDlg;//ダイアログハンドル
HBITMAPm_hBmpBackBuffer;//波形描写用のバックバッファ
HPENm_arrhPen[EPI_Total];//ペンの配列
QueWaveSamplem_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 …
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;;
}
「mp3再生中のISampleGrabbe」の回答画像4
    • good
    • 0
この回答へのお礼

返事が遅くなり申し訳ありません。
度々ありがとうございます。

取得する速度は若干早くなっておりますが、
やはり毎回取得するのは難しいようですね。
現在IGraphBuilder等を2つ作製し、
一つ目を先に波形データ作成用として無音で再生させ、バッファに貯めていきます。
出来た波形データ表示しながら少し遅れて二つ目を再生しようと考えています。
これで好きな場所の波形データを取得する事が出来ると思うのですが、
出来た波形データの現在再生中の位置を探し出す方法が考えつかず困っております。

お礼日時:2009/02/12 19:06

 こんにちは。



 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 …
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 …
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;
}
    • good
    • 0
この回答へのお礼

プログラムまで書いていただきありがとうございます。

早速組み込んでみたのですが結果は変わらず、
現在再生位置の波形データを取得する事はできませんでした。
取得したデータから現在再生中の位置を探し出すしかないのでしょうか。

お礼日時:2009/02/08 00:40

bufferのサイズが0.3秒分であれば0.3秒間は同じデータが取得されて正常だと思います。

    • good
    • 0
この回答へのお礼

そうだったのですか、勉強不足でした。
ありがとうございます。

お礼日時:2009/02/07 23:55

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!