No.2ベストアンサー
- 回答日時:
音声再生は
・再生用バッファが2つ確保される
・バッファの片方に、コーデックに合わせてデコードされた生波形データが生成される
・生成が終ったバッファの生波形データが再生開始される
・再生中に、バッファのもう片方にも、コーデックに合わせてデコードされた生波形データが生成される
・片方のバッファがすべて再生されると、すぐに、もう片方のバッファが再生される
・再生が既に終った方のバッファは、上書きしても大丈夫なので、そこに続きのデータをデコードした生波形データが生成される
・上記のように「片方のバッファで再生しつつ、もう片方のバッファでデコードを行う」と言う作業が繰り返される
と言う方法で再生されます。
GetCurrentBufferでは、上記のうち「2つのバッファのうち、今、再生に使ってる方」のデータのコピーが返されます。
片方のバッファサイズが「0.3秒分」であれば「どちらか片方のバッファが再生中の0.3秒間は、そのバッファのコピーが返され続ける」ので、0.3秒経たないと、続きのデータは取得出来ません。
因みに「取得した0.3秒間分のデータのうち、今の瞬間、どの位置を再生してるか?」は取得出来ません。
なるほど、音声再生はそのような方法で再生されていたのですね。
非常に分かりやすい説明をして頂き、ありがとうございます。
現在再生中の位置から少し先の波形データを取得して、
スペクトラムアナライザを表示しようと考えていたのですが、
再生中バッファの再生が終了しないと次のバッファが取得できないとの事なので、
少し難しい気がしてきました。
No.4
- 回答日時:
こんばんは。
御礼頂きました。その後、調査しているのですが、中々掴めていません。
改良してダイアログに表示してみたのですが、波形は入って来ている様です。
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;;
}
返事が遅くなり申し訳ありません。
度々ありがとうございます。
取得する速度は若干早くなっておりますが、
やはり毎回取得するのは難しいようですね。
現在IGraphBuilder等を2つ作製し、
一つ目を先に波形データ作成用として無音で再生させ、バッファに貯めていきます。
出来た波形データ表示しながら少し遅れて二つ目を再生しようと考えています。
これで好きな場所の波形データを取得する事が出来ると思うのですが、
出来た波形データの現在再生中の位置を探し出す方法が考えつかず困っております。
No.3
- 回答日時:
こんにちは。
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;
}
プログラムまで書いていただきありがとうございます。
早速組み込んでみたのですが結果は変わらず、
現在再生位置の波形データを取得する事はできませんでした。
取得したデータから現在再生中の位置を探し出すしかないのでしょうか。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Java javaのCSVデータ読込についてです 6 2022/07/02 10:58
- Excel(エクセル) EXCELの外部データ取得ができない 1 2023/03/23 09:03
- iPhone(アイフォーン) iPhone 5 2022/06/21 18:44
- スピーカー・コンポ・ステレオ 再生周波数を測定できるアプリを探しています。 質問失礼しますm(*_ _)m 現在、レコードやハイレ 4 2022/06/27 10:31
- その他(開発・運用・管理) bashで15分前と現在のエポックタイムの時間を取得したい 2 2023/02/01 19:10
- その他(コンピューター・テクノロジー) 50台の織機から回転数を取得・集計しモニターに表示したい 2 2022/11/05 15:48
- 就職 先のことが不安すぎる(主に就職活動) 3 2023/06/27 09:37
- その他(データベース) 業務用のデータベースサーバーの選び方について 4 2022/11/22 10:22
- ダイヤルアップ Raspberry Piでアナログモデム経由で音声再生 1 2022/05/20 18:01
- その他(AV機器・カメラ) カセットテープの音声データをPCに保存する方法を教えてくれた方にgood以上差し上げます(泣 2 2023/04/24 15:00
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C# シリアル通信でデータ受信...
-
RS-232Cでバイナリデータを受信...
-
socket: recvはいつ,どれだけ...
-
VC++2010 TCPIP通信の受信処理...
-
recv関数の受信結果について
-
MSCommでoutputできない
-
メールを数日後に自動返信
-
Macターミナルで実行中のプログ...
-
緯度、経度の 10進法と 60進法...
-
VB6での二重起動(複数起動)の制...
-
VBSの処理中一旦処理を止めて再...
-
プログラミング ソースコード
-
C言語で、メモリを解放しないで...
-
バックグラウンドのプロセスの...
-
エクセルのvbaで検索ボタンを作る
-
UdpClient 送信元のIPアドレ...
-
プロセスIDの取得方法について
-
Webプログラムってネイティブア...
-
MACで動く実行ファイルをWindow...
-
なんかC言語でプログラム書いて...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C# シリアル通信でデータ受信...
-
WriteFile()でのデータ送信がで...
-
winsockでソケット通信の開発を...
-
「スイッチングハブのバッファ...
-
socket: recvはいつ,どれだけ...
-
シリアル通信の出力バッファと...
-
Connection reset by peer
-
RS232C通信(PC⇔PLC)
-
RS-232Cでバイナリデータを受信...
-
WaitForMultipleObjects関数の...
-
Linuxでのシリアル通信について...
-
rs232cでの受信データ(mscomm)...
-
UDP処理のエラーについて
-
SocketのSend関数でのCLOSEの検...
-
ソケット通信内 read関数について
-
recv関数の受信結果について
-
シリアルポート通信
-
MSCommでoutputできない
-
SerialPortのDataReceivedイベ...
-
COMポートの同時オープン同時読...
おすすめ情報