Запись потока
Клиент вызывает методы в интерфейсе IAudioCaptureClient для чтения захваченных данных из буфера конечной точки. Клиент использует буфер конечной точки с подсистемой аудио в общем режиме и аудиоустройством в монопольном режиме. Чтобы запросить буфер конечной точки определенного размера, клиент вызывает метод IAudioClient::Initialize . Чтобы получить размер выделенного буфера, который может отличаться от запрошенного размера, клиент вызывает метод IAudioClient::GetBufferSize.
Чтобы переместить поток захваченных данных через буфер конечной точки, клиент также вызывает метод IAudioCaptureClient::GetBuffer и метод IAudioCaptureClient::ReleaseBuffer. Клиент обращается к данным в буфере конечной точки в виде ряда пакетов данных. Вызов GetBuffer извлекает следующий пакет захваченных данных из буфера. После чтения данных из пакета клиент вызывает ReleaseBuffer , чтобы освободить пакет и сделать его доступным для получения более захваченных данных.
Размер пакета может отличаться от одного вызова GetBuffer к следующему. Перед вызовом GetBuffer клиент имеет возможность вызова метода IAudioCaptureClient::GetNextPacketSize, чтобы получить размер следующего пакета заранее. Кроме того, клиент может вызвать метод IAudioClient::GetCurrentPadding , чтобы получить общий объем захваченных данных, доступных в буфере. В любой момент размер пакета всегда меньше или равен общему объему захваченных данных в буфере.
Во время каждого прохождения обработки клиент может обрабатывать захваченные данные одним из следующих способов:
- Клиент также вызывает GetBuffer и ReleaseBuffer, считывая один пакет с каждой парой вызовов, пока GetBuffer не возвращает AUDCNT_S_BUFFEREMPTY, указывая, что буфер пуст.
- Клиент вызывает GetNextPacketSize перед каждой парой вызовов GetBuffer и ReleaseBuffer, пока GetNextPacketSize не сообщает размер пакета 0, указывающий, что буфер пуст.
Два метода дают эквивалентные результаты.
В следующем примере кода показано, как записать аудиопоток с устройства записи по умолчанию:
//-----------------------------------------------------------
// Record an audio stream from the default audio capture
// device. The RecordAudioStream function allocates a shared
// buffer big enough to hold one second of PCM audio data.
// The function uses this buffer to stream data from the
// capture device. The main 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_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
HRESULT RecordAudioStream(MyAudioSink *pMySink)
{
HRESULT hr;
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
REFERENCE_TIME hnsActualDuration;
UINT32 bufferFrameCount;
UINT32 numFramesAvailable;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioClient *pAudioClient = NULL;
IAudioCaptureClient *pCaptureClient = NULL;
WAVEFORMATEX *pwfx = NULL;
UINT32 packetLength = 0;
BOOL bDone = FALSE;
BYTE *pData;
DWORD flags;
hr = CoCreateInstance(
CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
EXIT_ON_ERROR(hr)
hr = pEnumerator->GetDefaultAudioEndpoint(
eCapture, 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)
// Get the size of the allocated buffer.
hr = pAudioClient->GetBufferSize(&bufferFrameCount);
EXIT_ON_ERROR(hr)
hr = pAudioClient->GetService(
IID_IAudioCaptureClient,
(void**)&pCaptureClient);
EXIT_ON_ERROR(hr)
// Notify the audio sink which format to use.
hr = pMySink->SetFormat(pwfx);
EXIT_ON_ERROR(hr)
// Calculate the actual duration of the allocated buffer.
hnsActualDuration = (double)REFTIMES_PER_SEC *
bufferFrameCount / pwfx->nSamplesPerSec;
hr = pAudioClient->Start(); // Start recording.
EXIT_ON_ERROR(hr)
// Each loop fills about half of the shared buffer.
while (bDone == FALSE)
{
// Sleep for half the buffer duration.
Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2);
hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr)
while (packetLength != 0)
{
// Get the available data in the shared buffer.
hr = pCaptureClient->GetBuffer(
&pData,
&numFramesAvailable,
&flags, NULL, NULL);
EXIT_ON_ERROR(hr)
if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
{
pData = NULL; // Tell CopyData to write silence.
}
// Copy the available capture data to the audio sink.
hr = pMySink->CopyData(
pData, numFramesAvailable, &bDone);
EXIT_ON_ERROR(hr)
hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
EXIT_ON_ERROR(hr)
hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr)
}
}
hr = pAudioClient->Stop(); // Stop recording.
EXIT_ON_ERROR(hr)
Exit:
CoTaskMemFree(pwfx);
SAFE_RELEASE(pEnumerator)
SAFE_RELEASE(pDevice)
SAFE_RELEASE(pAudioClient)
SAFE_RELEASE(pCaptureClient)
return hr;
}
В предыдущем примере функция RecordAudioStream принимает один параметр, pMySink
который является указателем на объект, принадлежащий к клиентскому классу MyAudioSink, с двумя функциями, CopyData и SetFormat. Пример кода не включает реализацию MyAudioSink, так как:
- Ни один из членов класса не взаимодействует напрямую с любым из методов в интерфейсах в WASAPI.
- Класс можно реализовать различными способами в зависимости от требований клиента. (Например, он может записывать данные записи в WAV-файл.)
Однако сведения об операции двух методов полезны для понимания примера.
Функция CopyData копирует указанное количество звуковых кадров из указанного расположения буфера. Функция RecordAudioStream использует функцию CopyData для чтения и сохранения звуковых данных из общего буфера. Функция SetFormat указывает формат функции CopyData, используемой для данных.
Если объект MyAudioSink требует дополнительных данных, функция CopyData выводит значение FALSE через третий параметр, который в предыдущем примере кода является указателем на переменную bDone
. Если объект MyAudioSink содержит все необходимые данные, функция CopyData задает bDone
значение TRUE, что приводит к выходу программы из цикла в функции RecordAudioStream.
Функция RecordAudioStream выделяет общий буфер с длительностью 1 секунды. (Выделенный буфер может иметь немного более длинную длительность.) В главном цикле вызов функции спящего режима Windows приводит к тому, что программа будет ждать половину секунды. В начале каждого вызова спящего режима общий буфер пуст или почти пуст. К тому времени, когда вызов спящего режима возвращается, общий буфер составляет около половины, заполненной данными записи.
После вызова метода IAudioClient::Initialize поток остается открытым, пока клиент не освобождает все ссылки на интерфейс IAudioClient и все ссылки на интерфейсы служб, полученные клиентом с помощью метода IAudioClient::GetService. Последний вызов выпуска закрывает поток.
См. также