Поделиться через


Запись потока

Клиент вызывает методы в интерфейсе 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. Последний вызов выпуска закрывает поток.

Управление потоками