

bool RecordWMA::Initialize(LPCOLESTR savepath, HWND notice, UINT message) { : // キャプチャソースフィルタを取得してフィルタグラフに登録 // キャプチャソースフィルタの出力ピンに利用可能なバッファサイズ等を指示 CComPtr<IPin> out; capture_->FindPin(audioCapture, PINDIR_OUTPUT, &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, TRUE, 0, &out); CComQIPtr<IAMBufferNegotiation> nego(out); ALLOCATOR_PROPERTIES prop; prop.cBuffers = 8; // バッファの個数 prop.cbBuffer = long(44100 * 4 * 0.1); // バッファサイズ【(44.10kHz * stereo[2ch] * 16bit) * 0.1秒分のデータを格納可なサイズ】 prop.cbAlign = -1; // アラインメント(お任せ指定) prop.cbPrefix = -1; // プレフィックスのサイズ(お任せ指定) nego->SuggestAllocatorProperties(&prop); : }”capture_->FindPin” でキャプチャソースフィルタのキャプチャピンを取得しています。『DirectShowを探究しよー! 9』 で登場した getPin と役割は同じです。
enum { eTIMER_ID = 100, eMSG_NOTIFYVOLUMU = WM_APP }; static INT_PTR onInitDialog(HWND dlg, WPARAM w, LPARAM l) { : // 録音機能初期化 USES_CONVERSION; data->rec_.Initialize(T2OLE(data->tempfile_), dlg, eMSG_NOTIFYVOLUMU); data->recording_ = false; // 音量メーターの初期設定 ::SendDlgItemMessage(dlg, IDC_PROGRESS_METER, PBM_SETRANGE, 0, MAKELPARAM(0, SHRT_MAX)); ::SendDlgItemMessage(dlg, IDC_PROGRESS_METER, PBM_SETPOS, 0, 0); return FALSE; } static INT_PTR onNotifyVolume(HWND dlg, WPARAM w, LPARAM l) { ::SendDlgItemMessage(dlg, IDC_PROGRESS_METER, PBM_SETPOS, w, 0); return TRUE; } static INT_PTR CALLBACK DialogProc(HWND dlg, UINT msg, WPARAM w, LPARAM l) { switch (msg) { case WM_INITDIALOG: return onInitDialog(dlg, w, l); : case eMSG_NOTIFYVOLUMU: return onNotifyVolume(dlg, w, l); } return FALSE; }『DirectShowを探究しよー! 15』 から変更のある関数のみ抜き出しています。実際に追加編集されたコードは背景色が になってる部分です。
bool RecordWMA::Initialize(LPCOLESTR savepath, HWND notice, UINT message) { : // サンプルグラバをフィルタグラフに登録 // サンプルグラバが受け付けるメディアフォーマットを指定 // サンプルデータをコールバック経由で取得するように設定 CComPtr<SGCallback> cb(new SGCallback); cb->SetNoticeWindow(notice, message); // 通知先windowと通知メッセージidを指定 sample->SetCallback(cb, 1); // サンプル取得時に SGCallback の BufferCB() を呼び出す様に指示 : }”SGCallback” という新たなクラスが出てきました。
#include <qedit.h> class SGCallback : public ISampleGrabberCB { private: ULONG ref_; // com参照カウンタ HWND notice_; // 通知先window UINT message_; // 通知メッセージsig public: SGCallback(void) : notice_(NULL), ref_(0) {} ~SGCallback(void) {} STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { return E_NOINTERFACE; } STDMETHODIMP_(ULONG) AddRef() { return ++ref_; } STDMETHODIMP_(ULONG) Release() { if (--ref_ > 0) { return ref_; } delete this; return 0; } virtual HRESULT STDMETHODCALLTYPE BufferCB(double SampleTime, BYTE *pBuffer, long BufferLen) { if (BufferLen > 0 && notice_ != NULL) { // 【(16Bit[-32768 ~ 0(==無音) ~ 32767] * 2channel) * n】 // で構成されたサンプルデータが渡されてくる(『DirectShowを探究しよー! 18』 で其うフォーマット指定しました) unsigned long long int value = 0; const short* p = reinterpret_cast<short*>(pBuffer); const short* end = reinterpret_cast<short*>(pBuffer + BufferLen); for (; p < end; ++p) { value += abs(*p); // overflow した場合は知らん } ::PostMessage(notice_, message_, WPARAM(value / (BufferLen / sizeof(short))), 0); // 平均値を通知 } return S_OK; } virtual HRESULT STDMETHODCALLTYPE SampleCB(double SampleTime, IMediaSample *pSample) { // 使わないので実装しない return S_OK; } void SetNoticeWindow(HWND notice, UINT message) { notice_ = notice; message_ = message; } };すみません。↑で 「俺が作った」 と言っている様に唯の自作クラスです。特に別に凄い事があるわけではありません。
bool RecordWMA::Initialize(LPCOLESTR savepath, HWND notice, UINT message) { : // フィルタを繋ぐ capture_->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, audioCapture, grabberFilter, muxFilter); : }
bool RecordWMA::Initialize(LPCOLESTR savepath) { : // サンプルグラバをフィルタグラフに登録 CComPtr<IBaseFilter> grabberFilter; grabberFilter.CoCreateInstance(CLSID_SampleGrabber); graph->AddFilter(grabberFilter, L"sample grabber"); }CoCreateInstance して AddFilter、もう見慣れた形のコードです。
#include <qedit.h> bool RecordWMA::Initialize(LPCOLESTR savepath) { : // サンプルグラバをフィルタグラフに登録 CComPtr<IBaseFilter> grabberFilter; grabberFilter.CoCreateInstance(CLSID_SampleGrabber); graph->AddFilter(grabberFilter, L"sample grabber"); // サンプルグラバが受け付けるメディアフォーマットを指定 AM_MEDIA_TYPE mt = { 0 }; WAVEFORMATEX wf = { 0 }; mt.majortype = MEDIATYPE_Audio; // Audio データを受け付けるよ mt.subtype = MEDIASUBTYPE_PCM; // もっと詳しく言えば、PCM audio フォーマットのデータを受け付けるよ mt.formattype = FORMAT_WaveFormatEx; // 更に詳しい情報は WAVEFORMATEX 構造体で指示するので pbFormat を見て mt.cbFormat = sizeof(wf); // pbFormat に格納しているデータのサイズ mt.pbFormat = reinterpret_cast<BYTE*>(&wf); // ブツはコイツだ wf.wFormatTag = WAVE_FORMAT_PCM; // 欲しいデータは PCM 形式だ wf.nChannels = 2; // stereo(2channel)で wf.nSamplesPerSec = 44100; // サンプリングレートは 44.1KHz wf.wBitsPerSample = 16; // 1サンプルの情報は2Byte品質で wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; // お約束1 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; // お約束2 CComQIPtr<ISampleGrabber> sample(grabberFilter); sample->SetMediaType(&mt); : }”ISampleGrabber” てのは ”サンプルグラバ” フィルタの機能にアクセスするためのインターフェイスです。”サンプルグラバ” とのやり取りはこのインターフェイスを使って行います。


#include <tchar.h> #include <windows.h> #include <commdlg.h> #include <commctrl.h> #include "RecordWMA.h" #include "resource.h" // resource.h 内で以下を定義 //#define IDD_SOUNDRECORDER 101 // 「サウンド レコーダー」Dialog ID //#define IDC_BUTTON_RECORD 1001 // 「録音の開始」Button ID //#define IDC_STATIC_TIME 1002 // 「録音経過時間」Static Text ID //#define IDC_PROGRESS_METER 1003 // 「音量メーター」Progress Control ID #pragma comment(lib, "comctl32.lib") enum { eTIMER_ID = 100 }; struct USERDATA { _TCHAR tempfile_[_MAX_PATH]; // 作業用ファイルパス RecordWMA rec_; // 録音クラス bool recording_; // true=録音中、false=待機中 }; static bool getSaveAsFileName(HWND owner, LPTSTR path) { _TCHAR t[_MAX_PATH]; _tcscpy(t, _T("record.wma")); OPENFILENAME ofn = {0}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = owner; ofn.lpstrFile = t; ofn.nMaxFile = _countof(t); ofn.lpstrFilter = _T("Windows Media オーディオ ファイル(*.wma)\0*.wma\0\0"); ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLESIZING; ofn.lpstrDefExt = _T("wma"); if (::GetSaveFileName(&ofn)) { _tcscpy(path, t); return true; } return false; } static INT_PTR onInitDialog(HWND dlg, WPARAM w, LPARAM l) { USERDATA* data = new USERDATA; // USERDATA を dialog と結びつける ::SetWindowLongPtr(dlg, DWLP_USER, reinterpret_cast<LONG_PTR>(data)); // 作業用ファイルパスを求める _TCHAR tempdir[_MAX_PATH]; ::GetTempPath(_countof(tempdir), tempdir); ::GetTempFileName(tempdir, _T("sr"), 0, data->tempfile_); // 録音機能初期化 USES_CONVERSION; data->rec_.Initialize(T2OLE(data->tempfile_)); data->recording_ = false; return FALSE; } static INT_PTR onDestroy(HWND dlg, WPARAM w, LPARAM l) { USERDATA* data = reinterpret_cast<USERDATA*>(::SetWindowLongPtr(dlg, DWLP_USER, NULL)); data->rec_.Stop(); // 録音停止 ::DeleteFile(data->tempfile_); // 作業用ファイル削除 delete data; return TRUE; } static INT_PTR onTimer(HWND dlg, WPARAM w, LPARAM l) { if (w == eTIMER_ID) { USERDATA* data = reinterpret_cast<USERDATA*>(::GetWindowLongPtr(dlg, DWLP_USER)); if (data->recording_) { LONGLONG length; data->rec_.GetLength(length); length = (length + 5000000) / 10000000; // 100 nanoseconds -> 1 seconds _TCHAR t[32]; _stprintf(t, _T("%lld:%02lld:%02lld"), length / 3600, (length / 60) % 60, length % 60); ::SetDlgItemText(dlg, IDC_STATIC_TIME, t); } return TRUE; } return FALSE; } static INT_PTR onCancel(HWND dlg, WPARAM w, LPARAM l) { const UINT code = HIWORD(w); if (code == 0 || code == 1 || code == BN_CLICKED) { ::EndDialog(dlg, IDCANCEL); return TRUE; } return FALSE; } static INT_PTR onButtonRecord(HWND dlg, WPARAM w, LPARAM l) { const UINT code = HIWORD(w); if (code == 0 || code == 1 || code == BN_CLICKED) { USERDATA* data = reinterpret_cast<USERDATA*>(::GetWindowLongPtr(dlg, DWLP_USER)); if (!data->recording_) { if (data->rec_.Record()) { // 録音開始 ::SetTimer(dlg, eTIMER_ID, 1000, NULL); ::SetDlgItemText(dlg, IDC_BUTTON_RECORD, _T("録音の停止")); data->recording_ = true; } } else { data->recording_ = false; data->rec_.Pause(); // 録音一時停止 _TCHAR path[_MAX_PATH]; if (getSaveAsFileName(dlg, path)) { data->rec_.Stop(); // 録音終了 if (::MoveFileEx(data->tempfile_, path, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) { ::EndDialog(dlg, IDOK); // 終了 return TRUE; } } ::SetDlgItemText(dlg, IDC_BUTTON_RECORD, _T("録音の再開")); } return TRUE; } return FALSE; } static INT_PTR CALLBACK DialogProc(HWND dlg, UINT msg, WPARAM w, LPARAM l) { switch (msg) { case WM_INITDIALOG: return onInitDialog(dlg, w, l); case WM_DESTROY: return onDestroy(dlg, w, l); case WM_TIMER: return onTimer(dlg, w, l); case WM_COMMAND: switch (LOWORD(w)) { case IDCANCEL: return onCancel(dlg, w, l); case IDC_BUTTON_RECORD: return onButtonRecord(dlg, w, l); } break; } return FALSE; } int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { INITCOMMONCONTROLSEX cc; cc.dwSize = sizeof(cc); cc.dwICC = ICC_PROGRESS_CLASS; ::InitCommonControlsEx(&cc); ::CoInitialize(NULL); ::DialogBox(hInstance, MAKEINTRESOURCE(IDD_SOUNDRECORDER), NULL, &DialogProc); ::CoUninitialize(); }環境によっては 「必要なライブラリに link されてない」 とか error が出るかもしれませんが、適当に対応してください。


#include <tchar.h> #include <dshow.h> #include <atlbase.h> #include <dshowasf.h> // for IConfigAsfWriter #include <wmsysprf.h> // for WMProfile_V80_64StereoAudio #pragma comment(lib, "strmiids.lib") class RecordWMA { private: CComPtr<ICaptureGraphBuilder2> capture_; // キャプチャグラフビルダー CComQIPtr<IMediaControl> control_; // メディアコントロール public: RecordWMA() {} ~RecordWMA() { Stop(); } bool Initialize(LPCOLESTR savepath); bool Record(); bool Stop(); bool Pause(); // 一時停止 bool GetLength(LONGLONG& length); // 現在の録音時間を取得 private: bool getAudioDevice(IBaseFilter** audioDevice); }; bool RecordWMA::Initialize(LPCOLESTR savepath) { Stop(); capture_.Release(); control_.Release(); // キャプチャグラフビルダー作成 capture_.CoCreateInstance(CLSID_CaptureGraphBuilder2); // フィルタグラフ作成 CComPtr<IGraphBuilder> graph; graph.CoCreateInstance(CLSID_FilterGraph); // 対象とするフィルタグラフを ICaptureGraphBuilder2 に設定 capture_->SetFiltergraph(graph); // キャプチャソースフィルタを取得してフィルタグラフに登録 CComPtr<IBaseFilter> audioCapture; getAudioDevice(&audioCapture); graph->AddFilter(audioCapture, L"audio capture source")); // ファイル保存情報指定 CComPtr<IBaseFilter> muxFilter; capture_->SetOutputFileName(&MEDIASUBTYPE_Asf, savepath, &muxFilter, NULL); // 「WM ASF Writer」の設定 CComQIPtr<IConfigAsfWriter> config(muxFilter); config->ConfigureFilterUsingProfileGuid(WMProfile_V80_64StereoAudio); // フィルタを繋ぐ capture_->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, audioCapture, NULL, muxFilter); // 録音期間を指示 REFERENCE_TIME end = MAXLONGLONG; capture_->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, audioCapture, NULL, &end, 0, 0); // 使い回せるように IMediaControl インターフェイスを取得して確保しとく control_ = graph; return true; } bool RecordWMA::Record() { if (control_ == NULL) { return false; } // 録音を開始するように指示する OAFilterState state; control_->GetState(1000, &state); if (state != State_Running) { // 現在録音が開始されていないなら control_->Run(); // 録音開始! } return true; } bool RecordWMA::Stop() { if (control_ == NULL) { return false; } // 録音を停止するように指示する OAFilterState state; control_->GetState(1000, &state); if (state != State_Stopped) { // 現在停止状態でないなら control_->Stop(); // 録音停止! } return true; } bool RecordWMA::Pause() { if (control_ == NULL) { return false; } // 録音を一時停止するように指示する OAFilterState state; control_->GetState(1000, &state); if (state == State_Running) { // 現在録音中なら control_->Pause(); // ポーズ! } return true; } bool RecordWMA::GetLength(LONGLONG& length) { if (capture_ == NULL) { return false; } // ICaptureGraphBuilder2::SetFiltergraph() で設定したフィルタグラフを取得 CComPtr<IGraphBuilder> graph; capture_->GetFiltergraph(&graph); // 現在位置を取得(Time Format はデフォルトの Reference time という前提) CComQIPtr<IMediaSeeking> seek(graph); seek->GetCurrentPosition(&length); return true; } bool RecordWMA::getAudioDevice(IBaseFilter** audioDevice) { assert(*audioDevice == NULL); // Windows に「デバイス列挙機能を備えたクラス呉れ」と要求する CComPtr<ICreateDevEnum> deviceEnum; if (FAILED(deviceEnum.CoCreateInstance(CLSID_SystemDeviceEnum))) { return false; } // 「音声入力デバイスを列挙してくれ」と具体的に要求 CComPtr<IEnumMoniker> enumMoniker; if (FAILED(deviceEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory, &enumMoniker, 0))) { return false; } // (メンドクサイので)列挙された内の最初の音声入力デバイスを対象デバイスとする enumMoniker->Reset(); CComPtr<IMoniker> moniker; if (FAILED(enumMoniker->Next(1, &moniker, NULL))) { return false; // 音声入力デバイスは1つも存在しないってよ… } // 「見つかったデバイス用のキャプチャフィルタ呉れ」と要求 if (FAILED(moniker->BindToObject(0, 0, IID_IBaseFilter, reinterpret_cast<void**>(audioDevice)))) { return false; } // 「ありがとう」とお礼を言って去る return true; }とりあえずしてみました。録音の基本機能はこれで完成したぜ!ヤホーーーイ!
bool initialize(LPCOLESTR savepath) { : // 『DirectShowを探究しよー! 10』 と重複部分なので省略 // ★ お助けマンに録音期間を指示 REFERENCE_TIME end = MAXLONGLONG; capture->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, audioCapture, NULL, &end, 0, 0); // 録音開始 record(capture); const _TCHAR * const message = _T("メッセージボックスを閉じると録音を停止します。"); ::MessageBox(NULL, message, _T("録音中..."), MB_ICONINFORMATION | MB_OK); // 録音停止 stop(capture); return true; }とりあえずしてみました。これでもう録音できます。
// ★ お助けマンに録音期間を指示
REFERENCE_TIME end = MAXLONGLONG;
capture->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, audioCapture, NULL, &end, 0, 0);
MSDN 見てもらうのが一番だろうけど、新しく出たこの処理について軽く解説しとく。#include <dshow.h> #include <atlbase.h> #pragma comment(lib, "strmiids.lib") // 録音開始処理 bool record(ICaptureGraphBuilder2* capture) { // ICaptureGraphBuilder2::SetFiltergraph() で設定したフィルタグラフを取得 CComPtr<IGraphBuilder> graph; capture->GetFiltergraph(&graph); // 録音を開始するように指示する CComQIPtr<IMediaControl> control(graph); OAFilterState state; control->GetState(1000, &state); if (state != State_Running) { // 現在録音が開始されていないなら control->Run(); // 録音開始! } return true; } // 録音終了処理 bool stop(ICaptureGraphBuilder2* capture) { // ICaptureGraphBuilder2::SetFiltergraph() で設定したフィルタグラフを取得 CComPtr<IGraphBuilder> graph; capture->GetFiltergraph(&graph); // 録音を停止するように指示する CComQIPtr<IMediaControl> control(graph); OAFilterState state; control->GetState(1000, &state); if (state != State_Stopped) { // 現在停止状態でないなら control->Stop(); // 録音停止! } return true; }新たに ”IMediaControl” てのが出てきました。
#include <dshow.h> #include <atlbase.h> #include <dshowasf.h> // for IConfigAsfWriter #include <wmsysprf.h> // for WMProfile_V80_64StereoAudio #pragma comment(lib, "strmiids.lib") bool initialize(LPCOLESTR savepath) { // ★ キャプチャグラフビルダー作成 CComPtr<ICaptureGraphBuilder2> capture; capture.CoCreateInstance(CLSID_CaptureGraphBuilder2); // フィルタグラフ作成 CComPtr<IGraphBuilder> graph; graph.CoCreateInstance(CLSID_FilterGraph); // ★ お助け依頼契約締結 capture->SetFiltergraph(graph); // キャプチャソースフィルタを取得してフィルタグラフに登録 CComPtr<IBaseFilter> audioCapture; getAudioDevice(&audioCapture); graph->AddFilter(audioCapture, L"audio capture source")); // ★ お助けマンに「asf 形式で保存したいので準備して」と依頼 CComPtr<IBaseFilter> muxFilter; capture->SetOutputFileName(&MEDIASUBTYPE_Asf, savepath, &muxFilter, NULL); // 依頼の結果、asf用のフィルタ「WM ASF Writer」を用意 + 保存先パスの設定をして貰える // 「WM ASF Writer」の設定 CComQIPtr<IConfigAsfWriter> config(muxFilter); config->ConfigureFilterUsingProfileGuid(WMProfile_V80_64StereoAudio); // ★ お助けマンに「フィルタを繋いで」と依頼 capture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, audioCapture, NULL, muxFilter); return true; }※各関数の詳しい説明は MSDN で補完してね。
#include <dshow.h> #include <atlbase.h> #include <dshowasf.h> // for IConfigAsfWriter #include <wmsysprf.h> // for WMProfile_V80_64StereoAudio #pragma comment(lib, "strmiids.lib") bool initialize(LPCOLESTR savepath) { // フィルタグラフ作成 CComPtr<IGraphBuilder> graph; graph.CoCreateInstance(CLSID_FilterGraph); // キャプチャソースフィルタを取得してフィルタグラフに登録 CComPtr<IBaseFilter> audioCapture; getAudioDevice(&audioCapture); graph->AddFilter(audioCapture, L"audio capture source")); // コンプレッションフィルタ兼ファイルライタである「WM ASF Writer」を取得してフィルタグラフに登録 CComPtr<IBaseFilter> muxFilter; muxFilter.CoCreateInstance(CLSID_WMAsfWriter)); graph->AddFilter(muxFilter, L"audio multiplexer")); // 出力先パス指定 CComQIPtr<IFileSinkFilter> sinkFilter(muxFilter); sinkFilter->SetFileName(savepath, NULL) // 「WM ASF Writer」の設定 CComQIPtr<IConfigAsfWriter> config(muxFilter); config->ConfigureFilterUsingProfileGuid(WMProfile_V80_64StereoAudio); // キャプチャソースフィルタと「WM ASF Writer」と繋ぐ CComPtr<IPin> capture_out, asfwriter_in; capture_out.Attach(getPin(audioCapture, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE, MEDIATYPE_Audio)); asfwriter_in.Attach(getPin(muxFilter, PINDIR_INPUT, GUID_NULL, MEDIATYPE_Audio)); graph->Connect(capture_out, asfwriter_in); return true; } // フィルタから指定のピンを取得する関数 IPin* getPin(IBaseFilter* filter, PIN_DIRECTION direction, GUID category, GUID mediatype) { CComPtr<IEnumPins> enumPins; // フィルタが持つピンを列挙 if (SUCCEEDED(filter->EnumPins(&enumPins))) { IPin* pin; while (enumPins->Next(1, &pin, NULL) == S_OK) { // 列挙されたピンを順番にckeckしていく PIN_DIRECTION pd; if (FAILED(pin->QueryDirection(&pd)) || pd != direction) { // 向き(出力 or 入力)が違う! pin->Release(); continue; } if (category != GUID_NULL) { // ピンカテゴリが指定されているのでcheck CComQIPtr<IKsPropertySet> prop(pin); GUID cate; DWORD ret; if (prop == NULL || FAILED(prop->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0, &cate, sizeof(GUID), &ret)) || cate != category ) { // アウト! pin->Release(); continue; } } bool hit = true; if (mediatype != GUID_NULL) { // major メディアタイプが指定されているのでcheck hit = false; CComPtr<IEnumMediaTypes> enummediatype; if (SUCCEEDED(pin->EnumMediaTypes(&enummediatype))) { AM_MEDIA_TYPE* mt; while (enummediatype->Next(1, &mt, NULL) == S_OK) { const GUID mjtype = mt->majortype; // ::DeleteMediaType(mt); ::CoTaskMemFree(mt->pbFormat); if (mt->pUnk != NULL) { mt->pUnk->Release(); } ::CoTaskMemFree(mt); if (mjtype == mediatype) { // 指定メディアタイプを許容してた! hit = true; break; } } } } if (hit) { // 有効なピンが見かった return pin; } pin->Release(); } } return NULL; }これが ”ICaptureGraphBuilder2” 様の助けを受けない場合の下準備の全容です。改めて言うけどエラー処理はきちんとしようね。

The Capture Graph Builder is a helper object for building video and audio capture graphs. Capture and editing applications can use this component to construct filter graphs.
要すると 「”ICaptureGraphBuilder2” はお助けマンだ」 って事らしいです。 実際にどう”お助け”してくれるかを見てみましょう。
// 「WM ASF Writer」フィルタ作成
CComPtr<IBaseFilter> muxFilter;
muxFilter.CoCreateInstance(CLSID_WMAsfWriter));
// IConfigAsfWriter を取得して保存フォーマットを設定
CComQIPtr<IConfigAsfWriter> config(muxFilter);
config->ConfigureFilterUsingProfileGuid(WMProfile_V80_64StereoAudio); // streo 44.1KHz 64kbps 音声形式で保存する様に設定
”IConfigAsfWriter” てのは「WM ASF Writer」が独自に用意したインターフェイスです。”IFileSinkFilter” と違って他の ”ファイルライタ” は基本持っていません。// CComQIPtr<IConfigAsfWriter> config(muxFilter); CComPtr<IConfigAsfWriter> config muxFilter.QueryInterface(&config);こう書いてるのと一緒です。
// 保存先パス
const WCHAR * const filepath = L"c:\\record.wma";
// 「WM ASF Writer」フィルタ作成
CComPtr<IBaseFilter> muxFilter;
muxFilter.CoCreateInstance(CLSID_WMAsfWriter));
// IFileSinkFilter を取得してファイルパスを設定
CComPtr<IFileSinkFilter> sinkFilter;
muxFilter->QueryInterface(IID_IFileSinkFilter, reinterpret_cast<void**>(&sinkFilter));
sinkFilter->SetFileName(filepath, NULL)
QueryInterface の説明要りますかね?誤解上等でも要りますかね?やっといた方がよいですか。左様ですか。
// muxFilter->QueryInterface(IID_IFileSinkFilter, reinterpret_cast<void**>(&sinkFilter));
muxFilter.QueryInterface(&sinkFilter);
「何が違うの?」 と興味が出た人は違いについて調べて見て下さい。「はぁ?」 と思った人はスルーしましょう。混乱するだけですから。#include <dshow.h> #include <atlbase.h> #pragma comment(lib, "strmiids.lib") bool initialize() { // フィルタグラフ作成 CComPtr<IGraphBuilder> graph; graph.CoCreateInstance(CLSID_FilterGraph); // キャプチャソースフィルタを取得してフィルタグラフに登録 CComPtr<IBaseFilter> audioCapture; getAudioDevice(&audioCapture); graph->AddFilter(audioCapture, L"audio capture source")); // コンプレッションフィルタ兼ファイルライタである「WM ASF Writer」を取得してフィルタグラフに登録 CComPtr<IBaseFilter> muxFilter; muxFilter.CoCreateInstance(CLSID_WMAsfWriter)); graph->AddFilter(muxFilter, L"audio multiplexer")); return true; }これだけです。エラー処理は一切無視してますが実際には真似しない様に。
#include <dshow.h> #include <atlbase.h> #pragma comment(lib, "strmiids.lib") bool getAudioDevice(IBaseFilter** audioDevice) { assert(*audioDevice == NULL); // Windows に「デバイス列挙機能を備えたクラス呉れ」と要求する CComPtr<ICreateDevEnum> deviceEnum; if (FAILED(deviceEnum.CoCreateInstance(CLSID_SystemDeviceEnum))) { return false; } // 「音声入力デバイスを列挙してくれ」と具体的に要求 CComPtr<IEnumMoniker> enumMoniker; if (FAILED(deviceEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory, &enumMoniker, 0))) { return false; } // (メンドクサイので)列挙された内の最初の音声入力デバイスを対象デバイスとする enumMoniker->Reset(); CComPtr<IMoniker> moniker; if (FAILED(enumMoniker->Next(1, &moniker, NULL))) { return false; // 音声入力デバイスは1つも存在しないってよ… } // 「見つかったデバイス用のキャプチャフィルタ呉れ」と要求 if (FAILED(moniker->BindToObject(0, 0, IID_IBaseFilter, reinterpret_cast<void**>(audioDevice)))) { return false; } // 「ありがとう」とお礼を言って去る return true; }コード内で CComPtr とか出てますが、このクラスは COM インターフェイスの扱いを助けてくれるクラスです。詳しくはググってくだせい。
#include <dshow.h> #pragma comment(lib, "strmiids.lib") IGraphBuilder* graph; ::CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&graph);”IGraphBuilder”、コイツが所謂 ”フィルタグラフ” です。

| ① | ソースフィルタ | ファイルからデータを読み込む |
| ② | パーサフィルタ | 読み込まれたデータを解析して画像と音声に分割、再生フレーム単位に分割等する |
| ③ | デコンプレッションフィルタ(画像) | 圧縮された画像データを展開する※上記例だと「Microsoft Video 1(CRAM)」⇒「RGB24」 |
| ④ | オーディオレンダラ | 音を鳴らす |
| ⑤ | エフェクトフィルタ | 渡されたデータを加工する※上記例だと「RGB24」⇒「RGB32」に色深度を変換(この時のマシンの画面色深度が32bitなので) |
| ⑥ | ビデオレンダラ | 画像表示 |
| 靡く赤旗 | |
|---|---|
| 赤井 | 最終マーク。鮮やかな逆転劇でしたね。 |
| あさひ | ああ。休日の8:15から街宣車活動で眠っている住民を叩き起こす。去ったと思い2度寝した30分後に再登場。そこから15分以上に渡る街頭演説。 …圧倒的だった。 |
| 赤井 | その街頭演説の内容も 『消費税はダメだ。そう思うならば共産グループ』 『アメリカの犬は嫌だ。そう思うならば共産グループ』 のループで15分以上ですからね。 |
| あさひ | 加えて、街頭演説が終了し静かになったと思わせた10分後に颯爽と街宣車で再再登場するサプライズ、そして騒音と共に再び去る。止めとしては十分だ。 |
| 赤井 | 清水の前では岡部の休日8:45からの街宣車活動なんて可愛く思えます。 |
| あさひ | ここまでされればもう眼が冴えて二度寝はできない。寝ようと粘ってみても時間の無駄にしかならん。 |
| あさひ | 10:15に桂禁止が街宣車で来た時には『睡眠時間確保を諦めるきっかけを呉れてありがとう』て感じだったからな。 この事からも清水…というか共産党グループの凄さが判る。 |
| 赤井 | その清水がウィニングランで聴衆に応えています。清水は初優勝、共産グループは前回レースに続いての連続優勝です。 |
| 赤井 | 見事でした清水。そして、共産グループの底力を見せ付けたレースでした。 |
| : | |
| あさひ | そういえば…5号艇は何してたんだ? |
| ⑤ 北川 | 組織票主体で追い風にも乗った地味な活動してましたから…このレースでの見せ場なんてありませんでした。 |
| 3周 1マーク | |
|---|---|
| 赤井 | 3週目。トップ2号艇、4挺身離れて1号艇。このまま最後までいくのか!? |
| ② 岡部 | プライドに掛けても当選しなきゃいけない。ココで勝負を掛けるわよ! |
| ② 岡部 | 必殺!! 休日の8:45から街宣車活動~! |
| ② 岡部 | !? |
| 赤井 | あぁっと!ターンミス!? 2号艇大きくふくらんだぁ!! |
| あさひ | 素人が調子に乗るから…他人が不快感じない事を”普通”にヤってりゃ良いのによぉ… |
| あさひ | 休日早朝に叩き起こされた奴らが持っていた浮動票は諦めるしかないな。 |
| 赤井 | 大混戦! 勝負は最終2マークまでもつれた!! |
| 赤井 | 2号艇先マイ! |
| 赤井 | 1号艇はインに突っ込んだ!! |
| ② 岡部 | ナイトスクープ!ナイトスクープ!!ナイトスクープゥゥゥ!!! |
| ① 清水 | ・・・共産グループ伝統の”勝負賭け”を見せてやる!! |
| !? | |
| 赤井 | あぁッ!! |
| あさひ | これは…強烈だ。 |
| 赤井 | 1号艇逆転した? 逆転だ!! 半挺身前に出た!! |
| 赤井 | 清水今ゴールイン!! 続いて2号艇岡部が入りました。 |
| 赤井 | 『SG 最悪選挙戦王決定戦 in 大阪』を制したのは共産グループの清水隆史でした! |
| 126435 | |
| 2週 1マーク | |
|---|---|
| ⑥ 桂 | 岡部と尾立が暴走して荒れればとは思っていたが…本当にチャンスが回って来たわい! |
| ⑥ 桂 | このまま共産野郎も抜いてやるか。元松竹など…元吉本の敵では無い! |
| 赤井 | 2週1マーク。6号艇外に振った! 差しだ!! |
| ⑥ 桂 | 『小さなことからコツコツとぅ~~!』 |
| ① 清水 | !? 他人の褌で勝負してんじゃねぇよ!!! |
| 赤井 | 決まるか!!! |
| 赤井 | 未だだ! 1号艇耐えた!! |
| ⑥ 桂 | シツコイのぅ。お笑い100万票は吉本のモンなんじゃ。諦めい。 |
| ① 清水 | 馬鹿か。民主グループに所属している時点でお笑いだろうとなんだろうとお前の敵だよ! |
| ⑥ 桂 | フン、そんな事は疾うに判っておる。だから…ワシは既に”民主”である事は隠しとるわ!! |
| ⑥ 桂 | 小さなことからコツコツとぅ。今回比例代表で出馬した桂禁止です。桂禁止をよろしくお願いしますぅ! |
| ① 清水 | !? 所属グループ名言えよ! 『”民主から”比例代表で出馬した』 と誠実に伝えろよ!! |
| 赤井 | 2週バックストレート。6号艇が半挺身前に出た! 直線の伸びは6号艇が上だ!! |
| あさひ | 準優までとは伸びが違う。コイツ、グループ仲間から貰ったエースペラを温存してやがったな。 |
| 赤井 | 2週2マーク。6号艇はイン。 |
| 赤井 | 1号艇は…外! ツケマイだ!! |
| ⑥ 桂 | 小さなことからコツコツとぅ~。西川きょしの志を継いだ桂禁止をよろしくお願いしますゥ! |
| ① 清水 | 他人の威光で勝とうとする様な奴に… |
| ① 清水 | 所属グループに誇りを持っていない様な奴に… |
| ① 清水 | 真っ赤に染まった俺が敗けるかよ!!! |
| ⑥ 桂 | なんだと!? |
| 赤井 | 強烈! 1号艇のツケマイが決まったァ!! |
| 216435 | |
| スタートライン | |
|---|---|
| 赤井 | さあ、発走の合図と共に6選手がピットから飛び出します。 |
| 赤井 | 各艇が一斉に飛び出した。全艇ピット離れは良好。これはどのような進入となるか。 |
| 赤井 | おっと。スタート展示とは異なり2号艇が枠主張している! これはこのまま入りそうだ。 |
| 赤井 | 6号艇が後方に引っ張った。それに続いて5号艇。 |
| 赤井 | 進入は枠なりです。隊形は2、2、2。インコースから枠なり進入でスタートです。 |
| 赤井 | 6号艇からしぶきが舞った。5号艇少し起こし遅れた! |
| 赤井 | 続けてイン各艇からも水柱が上がった! |
| 赤井 | 4号艇、6号艇が好スタート! |
| 赤井 | 1周1マーク、4号艇一気にマクリにいく! 1号艇突っ張る! |
| 赤井 | 2号艇は差しに廻る! 3号艇は…呑まれたか!! |
| ④ 尾立 | 厳しい選挙区なのに2人も立てるんじゃねぇ! バカオzaワァァァァ!! |
| ① 清水 | お前らミンスは3人で共喰いでもしてろ!! |
| ② 岡部 | ナァイトスクープゥゥゥ!!! |
| ③ 石川 | 信心が無い人達は余裕が無さ過ぎですね…折伏してあげましょうか? |
| 赤井 | 4号艇外に流れた! 1号艇は残った!! |
| 赤井 | 2号艇差してトップ! まくり差した6号艇が最内3番手! |
| 赤井 | 上位大勢は2番、1番、6番。スタートは正常。このまま2週目に入ります。 |
| 216435 | |
| 展示 | |
|---|---|
| 赤井 | さあ、未だ梅雨も空けず鬱陶しい天気の元、今回も最悪選挙戦王決定戦が此処大阪で開かれる事となりました。 |
| 赤井 | このレースは選挙戦でどれだけ迷惑行為・迷走行為を働いたかを競い合うレースです。 |
| あさひ | 誰も開催を望んでねぇんだがな… |
| 赤井 | 実況は赤井邦道。解説はあさひ梅克でお送りします。では先程行われた周回展示を振り返ってみましょう。 |
| 赤井 | 1号艇、清水隆史。共産グループ所属。スタート展示では1枠インに入りました。 |
| あさひ | 元お笑いの元大阪市議。お笑い100万票で”先生”になった類かね。ただ、国政選挙で共産グループが入り込む余地は大阪には無いからなあ。 形振り構わず無茶してくるだろう。優勝の最右翼だ。 |
| 赤井 | 2号艇、岡部マイ。民主グループ所属。スタート展示では4枠カドを取りました。 |
| あさひ | ”知名度は一番在る”というプライドが邪魔してか、必死な姿を公衆に晒したくない感じだな。知名度があるから何とかなると思ってんだろう。 |
| 赤井 | 3号艇、石川崇宏。公明グループ所属。スタート展示では2枠に入りました。 |
| あさひ | 組織票を大量に持ってるから無難に流してレースを終えるだけだろう。政教分離糞喰らえって感じだな。2枠に入ったのもガツガツしていたのでは無く成行きで入っただけだ。 |
| 赤井 | 4号艇、尾立基行。民主グループ所属。スタート展示では3枠に入りました。 |
| あさひ | スタート展示なのに3号艇に落とされた岡部を内に入れなかったな。同じ民主グループなのに岡部を完全に敵視している。 |
| 赤井 | 5号艇、北川二成。自民グループ所属。 |
| あさひ | 組織票は纏まっているし今回は追い風も吹いている。大きなミスさえしなければ結果を出せるレースだ。賭けに出ず、枠なりにレースをするだけだろう。 |
| 赤井 | 6号艇、桂禁止。民主グループ比例代表派所属。大外6枠です。 |
| あさひ | 大外の6号艇、ペラも合っていないし整備で調整できる術も無い。お笑い100万票と民主グループの力が頼みだな。 |
| 赤井 | 以上、6名によって争われます。さあ、まもなくレース開始です。 |
struct KOZOTAI kozotai = { 0 };
とか
char buffer[10]; for (int i=0; i < 10; ++i) { buffer[i] = 0; }って処理を VC9(他Verでどうなのかは確認してないが) のコンパイラで最適化すると memset() に置き換えられちゃう可能性があるんだね。










| CPU | Core 2 Duo P8400 |
| メモリ | 1GB |