こんばんは。御礼頂きました。
その後、調査しているのですが、中々掴めていません。
改良してダイアログに表示してみたのですが、波形は入って来ている様です。
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;;
}
お礼
なるほど、音声再生はそのような方法で再生されていたのですね。 非常に分かりやすい説明をして頂き、ありがとうございます。 現在再生中の位置から少し先の波形データを取得して、 スペクトラムアナライザを表示しようと考えていたのですが、 再生中バッファの再生が終了しないと次のバッファが取得できないとの事なので、 少し難しい気がしてきました。