トレーニング
ラーニング パス
Azure Stream Analytics を使用してデータ ストリーミング ソリューションを実装する - Training
Azure Stream Analytics を使用してデータ ストリーミング ソリューションを実装する
このブラウザーはサポートされなくなりました。
Microsoft Edge にアップグレードすると、最新の機能、セキュリティ更新プログラム、およびテクニカル サポートを利用できます。
クライアントは IAudioRenderClient インターフェイス内のメソッドを呼び出して、レンダリング データをエンドポイント バッファーに書き込みます。 共有モード ストリームの場合、クライアントはエンドポイント バッファーをオーディオ エンジンと共有します。 排他モード ストリームの場合、クライアントはエンドポイント バッファーをオーディオ デバイスと共有します。 特定のサイズのエンドポイント バッファーを要求するために、クライアントは IAudioClient::Initialize メソッドを呼び出します。 割り当てられたバッファーのサイズ (要求されたサイズとは異なる可能性があります) を取得するために、クライアントは IAudioClient::GetBufferSize メソッドを呼び出します。
エンドポイント バッファーを介してレンダリング データのストリームを移動するために、クライアントは代わりに IAudioRenderClient::GetBuffer メソッドと IAudioRenderClient::ReleaseBuffer メソッドを呼び出します。 クライアントは、エンドポイント バッファー内のデータに一連のデータ パケットとしてアクセスします。 GetBuffer 呼び出しは、クライアントがレンダリング データを格納できるように、次のパケットを取得します。 パケットにデータを書き込んだ後、クライアントは ReleaseBuffer を呼び出して、完成したパケットをレンダリング キューに追加します。
レンダリング バッファーの場合、IAudioClient::GetCurrentPadding メソッドによって報告される埋め込み値は、バッファー内で再生するためにキューに格納されるレンダリング データの量を表します。 レンダリング アプリケーションでは、埋め込み値を使用して、以前に書き込まれたデータのうちオーディオ エンジンがまだバッファーから読み取っていないデータを上書きするリスクなしに、バッファーに安全に書き込むことができる、新しいデータの量を決定できます。 使用可能な領域は、単純にバッファー サイズから埋め込みサイズを引いた値です。 クライアントは、次の GetBuffer 呼び出しで、この使用可能な領域の一部またはすべてを表すパケット サイズを要求できます。
パケットのサイズは、オーディオ フレームで表されます。 PCM ストリーム内のオーディオ フレームは、再生または同時に (クロック ティック) 記録されるサンプルのセットです (ストリーム内のチャネルごとに 1 つのサンプルがセットに含まれています)。 したがって、オーディオ フレームのサイズは、ストリーム内のチャンネル数を乗算したサンプル サイズです。 たとえば、16 ビットのサンプルを含むステレオ (2 チャネル) ストリームのフレーム サイズは 4 バイトです。
次のコード例は、既定のレンダリング デバイスでオーディオ ストリームを再生する方法を示しています。
//-----------------------------------------------------------
// Play an audio stream on the default audio rendering
// device. The PlayAudioStream function allocates a shared
// buffer big enough to hold one second of PCM audio data.
// The function uses this buffer to stream data to the
// rendering device. The inner loop runs every 1/2 second.
//-----------------------------------------------------------
// REFERENCE_TIME time units per second and per millisecond
#define REFTIMES_PER_SEC 10000000
#define REFTIMES_PER_MILLISEC 10000
#define EXIT_ON_ERROR(hres) \
if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) \
{ (punk)->Release(); (punk) = NULL; }
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
HRESULT PlayAudioStream(MyAudioSource *pMySource)
{
HRESULT hr;
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
REFERENCE_TIME hnsActualDuration;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioClient *pAudioClient = NULL;
IAudioRenderClient *pRenderClient = NULL;
WAVEFORMATEX *pwfx = NULL;
UINT32 bufferFrameCount;
UINT32 numFramesAvailable;
UINT32 numFramesPadding;
BYTE *pData;
DWORD flags = 0;
hr = CoCreateInstance(
CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
EXIT_ON_ERROR(hr)
hr = pEnumerator->GetDefaultAudioEndpoint(
eRender, eConsole, &pDevice);
EXIT_ON_ERROR(hr)
hr = pDevice->Activate(
IID_IAudioClient, CLSCTX_ALL,
NULL, (void**)&pAudioClient);
EXIT_ON_ERROR(hr)
hr = pAudioClient->GetMixFormat(&pwfx);
EXIT_ON_ERROR(hr)
hr = pAudioClient->Initialize(
AUDCLNT_SHAREMODE_SHARED,
0,
hnsRequestedDuration,
0,
pwfx,
NULL);
EXIT_ON_ERROR(hr)
// Tell the audio source which format to use.
hr = pMySource->SetFormat(pwfx);
EXIT_ON_ERROR(hr)
// Get the actual size of the allocated buffer.
hr = pAudioClient->GetBufferSize(&bufferFrameCount);
EXIT_ON_ERROR(hr)
hr = pAudioClient->GetService(
IID_IAudioRenderClient,
(void**)&pRenderClient);
EXIT_ON_ERROR(hr)
// Grab the entire buffer for the initial fill operation.
hr = pRenderClient->GetBuffer(bufferFrameCount, &pData);
EXIT_ON_ERROR(hr)
// Load the initial data into the shared buffer.
hr = pMySource->LoadData(bufferFrameCount, pData, &flags);
EXIT_ON_ERROR(hr)
hr = pRenderClient->ReleaseBuffer(bufferFrameCount, flags);
EXIT_ON_ERROR(hr)
// Calculate the actual duration of the allocated buffer.
hnsActualDuration = (double)REFTIMES_PER_SEC *
bufferFrameCount / pwfx->nSamplesPerSec;
hr = pAudioClient->Start(); // Start playing.
EXIT_ON_ERROR(hr)
// Each loop fills about half of the shared buffer.
while (flags != AUDCLNT_BUFFERFLAGS_SILENT)
{
// Sleep for half the buffer duration.
Sleep((DWORD)(hnsActualDuration/REFTIMES_PER_MILLISEC/2));
// See how much buffer space is available.
hr = pAudioClient->GetCurrentPadding(&numFramesPadding);
EXIT_ON_ERROR(hr)
numFramesAvailable = bufferFrameCount - numFramesPadding;
// Grab all the available space in the shared buffer.
hr = pRenderClient->GetBuffer(numFramesAvailable, &pData);
EXIT_ON_ERROR(hr)
// Get next 1/2-second of data from the audio source.
hr = pMySource->LoadData(numFramesAvailable, pData, &flags);
EXIT_ON_ERROR(hr)
hr = pRenderClient->ReleaseBuffer(numFramesAvailable, flags);
EXIT_ON_ERROR(hr)
}
// Wait for last data in buffer to play before stopping.
Sleep((DWORD)(hnsActualDuration/REFTIMES_PER_MILLISEC/2));
hr = pAudioClient->Stop(); // Stop playing.
EXIT_ON_ERROR(hr)
Exit:
CoTaskMemFree(pwfx);
SAFE_RELEASE(pEnumerator)
SAFE_RELEASE(pDevice)
SAFE_RELEASE(pAudioClient)
SAFE_RELEASE(pRenderClient)
return hr;
}
前の例では、PlayAudioStream 関数は、2 つのメンバー関数 LoadData と SetFormat を持つクライアント定義クラス MyAudioSource に属するオブジェクトへのポインターである 1 つのパラメーター、pMySource
を受け取ります。 次の理由から、サンプル コードには MyAudioSource の実装は含まれません。
とは言え、2 つの関数の操作に関するいくつかの情報は、この例を理解するのに役立ちます。
LoadData 関数は、指定した数のオーディオ フレーム (最初のパラメーター) を指定したバッファー位置 (2 番目のパラメーター) に書き込みます。 (オーディオ フレームのサイズは、ストリーム内のチャンネル数にサンプル サイズを乗算したものです。) PlayAudioStream 関数は、LoadData を使用して、共有バッファーの一部をオーディオ データで埋めます。 SetFormat 関数は、データに使用する LoadData 関数の形式を指定します。 LoadData 関数は、指定したバッファー位置に少なくとも 1 つのフレームを書き込むことができますが、指定した数のフレームを書き込む前にデータが不足してしまった場合は、残りのフレームに無音を書き込みます。
LoadData は、指定されたバッファー位置への実データの少なくとも 1 つのフレーム (無音ではない) の書き込みに成功する限り、0 から 3 番目のパラメーターを出力します。これは、前のコード例では、flags
変数への出力ポインターです。 LoadData がデータから外れ、指定したバッファー位置に 1 つのフレームを書き込むことができない場合、バッファーに何も書き込まない (無音でもない) と、AUDCLNT_BUFFERFLAGS_SILENT 値が flags
変数に書き込まれます。 flags
変数は、この値を IAudioRenderClient::ReleaseBuffer メソッドに伝達します。このメソッドは、バッファー内の指定されたフレーム数を無音で埋めることで応答します。
IAudioClient::Initialize メソッドへの呼び出しで、前の例の PlayAudioStream 関数は、期間が 1 秒の共有バッファーを要求します。 (割り当てられたバッファーの期間が少し長くなる場合があります。) IAudioRenderClient::GetBuffer メソッドと IAudioRenderClient::ReleaseBuffer メソッドへの最初の呼び出しでは、IAudioClient::Start メソッドを呼び出してバッファーの再生を開始する前に、関数がバッファー全体を埋めます。
メイン ループ内では、関数は 0.5 秒間隔でバッファーの半分を繰り返し埋めます。 メイン ループでの Windows Sleep 関数への各呼び出しの直前に、バッファーが一杯またはほぼ一杯になります。 Sleep 呼び出しが返されると、バッファーの約半分が一杯になります。 LoadData 関数の最後の呼び出し後にループが終了し、flags
変数が AUDCLNT_BUFFERFLAGS_SILENT 値に設定されます。 この時点で、バッファーには少なくとも 1 フレームの実データが含まれており、半秒の実データが含まれている可能性があります。 残りのバッファーには無音が含まれています。 ループに続く Sleep 呼び出しは、残りのデータをすべて再生するのに十分な時間 (0.5 秒) を提供します。 データに続く無音は、IAudioClient::Stop メソッドへの呼び出しがオーディオ ストリームを停止する前に不要なサウンドを防ぎます。 Sleep の詳細については、Windows SDK のドキュメントを参照してください。
IAudioClient::Initialize メソッドへの呼び出しの後、クライアントが、IAudioClient インターフェイスへのすべての参照と、クライアントが IAudioClient::GetService メソッドを介して取得したサービス インターフェイスへのすべての参照を解放するまで、ストリームは開いたままとなります。 最後の Release 呼び出しでストリームが閉じられます。
前のコード例の PlayAudioStream 関数は、CoCreateInstance 関数を呼び出して、システム内のオーディオ エンドポイント デバイスの列挙子を作成します。 呼び出し元のプログラムが COM ライブラリを初期化するために CoCreateInstance 関数または CoInitializeEx 関数を以前に呼び出していない限り、CoCreateInstance 呼び出しは失敗します。 CoCreateInstance、CoCreateInstance、CoInitializeEx の詳細については、Windows SDK のドキュメントを参照してください。
トレーニング
ラーニング パス
Azure Stream Analytics を使用してデータ ストリーミング ソリューションを実装する - Training
Azure Stream Analytics を使用してデータ ストリーミング ソリューションを実装する