Delen via


Een stream weergeven

De client roept de methoden aan in de IAudioRenderClient interface om renderinggegevens naar een eindpuntbuffer te schrijven. Voor een stream in de gedeelde modus deelt de client de eindpuntbuffer met de audio-engine. Voor een exclusieve modusstream deelt de client de eindpuntbuffer met het audioapparaat. Als u een eindpuntbuffer van een bepaalde grootte wilt aanvragen, roept de client de IAudioClient::Initialize methode aan. De client roept de IAudioClient::GetBufferSize methode aan om de grootte van de toegewezen buffer op te halen. Deze kan afwijken van de aangevraagde grootte.

Om een stroom renderinggegevens afwisselend via de eindpuntbuffer te verplaatsen, roept de client de IAudioRenderClient::GetBuffer en IAudioRenderClient::ReleaseBuffer methoden aan. De client opent de gegevens in de eindpuntbuffer als een reeks gegevenspakketten. De GetBuffer aanroep haalt het volgende pakket op, zodat de client het kan vullen met renderinggegevens. Nadat de gegevens naar het pakket zijn geschreven, roept de client ReleaseBuffer aan om het voltooide pakket toe te voegen aan de renderingwachtrij.

Voor een renderingbuffer vertegenwoordigt de opvullingswaarde die wordt gerapporteerd door de IAudioClient::GetCurrentPadding-methode de hoeveelheid renderinggegevens die in de wachtrij worden geplaatst om in de buffer te worden afgespeeld. Een renderingtoepassing kan de opvullingswaarde gebruiken om te bepalen hoeveel nieuwe gegevens deze veilig naar de buffer kunnen schrijven zonder het risico dat eerder geschreven gegevens worden overschreven die de audio-engine nog niet uit de buffer heeft gelezen. De beschikbare ruimte is gewoon de buffergrootte minus de opvullingsgrootte. De client kan een pakketgrootte aanvragen die een deel van deze beschikbare ruimte vertegenwoordigt in de volgende GetBuffer aanroep.

De grootte van een pakket wordt uitgedrukt in audioframes. Een audioframe in een PCM-stream is een set voorbeelden (de set bevat één voorbeeld voor elk kanaal in de stream) die tegelijkertijd worden afgespeeld of opgenomen (kloktik). De grootte van een audioframe is dus de steekproefgrootte vermenigvuldigd met het aantal kanalen in de stream. De framegrootte voor een stereostream (2-kanaals) met 16-bits voorbeelden is bijvoorbeeld vier bytes.

In het volgende codevoorbeeld ziet u hoe u een audiostream kunt afspelen op het standaardrenderingsapparaat:

//-----------------------------------------------------------
// 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;
}

In het voorgaande voorbeeld heeft de functie PlayAudioStream één parameter, pMySource, een aanwijzer naar een object dat deel uitmaakt van een door de client gedefinieerde klasse MyAudioSource, met twee lidfuncties, LoadData en SetFormat. De voorbeeldcode bevat niet de implementatie van MyAudioSource omdat:

  • Geen van de klasseleden communiceert rechtstreeks met een van de methoden in de interfaces in WASAPI.
  • De klasse kan op verschillende manieren worden geïmplementeerd, afhankelijk van de vereisten van de client. (Het kan bijvoorbeeld de renderinggegevens lezen uit een WAV-bestand en on-the-fly conversie uitvoeren naar de streamindeling.)

Sommige informatie over de werking van de twee functies is echter handig voor het begrijpen van het voorbeeld.

De functie LoadData schrijft een opgegeven aantal audioframes (eerste parameter) naar een opgegeven bufferlocatie (tweede parameter). (De grootte van een audioframe is het aantal kanalen in de stream vermenigvuldigd met de steekproefgrootte.) De functie PlayAudioStream maakt gebruik van LoadData om delen van de gedeelde buffer te vullen met audiogegevens. De functie SetFormat geeft de indeling op voor de functie LoadData die moet worden gebruikt voor de gegevens. Als de functie LoadData ten minste één frame naar de opgegeven bufferlocatie kan schrijven, maar er geen gegevens meer zijn voordat het opgegeven aantal frames is geschreven, schrijft deze stilte naar de resterende frames.

Zolang LoadData slaagt in het schrijven van ten minste één frame van echte gegevens (geen stilte) naar de opgegeven bufferlocatie, wordt 0 uitgevoerd via de derde parameter, die in het voorgaande codevoorbeeld een uitvoeraanwijzer is naar de flags variabele. Wanneer LoadData geen gegevens bevat en zelfs geen enkel frame naar de opgegeven bufferlocatie kan schrijven, wordt er niets naar de buffer geschreven (zelfs geen stilte) en wordt de waarde AUDCLNT_BUFFERFLAGS_SILENT naar de flags variabele geschreven. De flags variabele brengt deze waarde over aan de methode IAudioRenderClient::ReleaseBuffer, die reageert door het opgegeven aantal frames in de buffer met stilte in te vullen.

In de aanroep van de methode IAudioClient::Initialize, vraagt de functie PlayAudioStream in het voorgaande voorbeeld een gedeelde buffer aan die een duur van één seconde heeft. (Bij de toegewezen buffer kan de duur iets langer zijn.) Bij de eerste aanroepen van de IAudioRenderClient::GetBuffer en IAudioRenderClient::ReleaseBuffer methoden, vult de functie de gehele buffer voordat deze de IAudioClient::Start methode aanroept om te beginnen met afspelen van de buffer.

Binnen de hoofdlus vult de functie de helft van de buffer iteratief op met intervallen van een halve seconde. Net voordat elke aanroep naar de functie Windows Slaapstand in de hoofdlus wordt aangeroepen, is de buffer vol of bijna vol. Wanneer de Sleep aanroep retourneert, is de buffer ongeveer half vol. De lus eindigt na de laatste aanroep van de functie LoadData, die de variabele flags instelt op de waarde AUDCLNT_BUFFERFLAGS_SILENT. Op dat moment bevat de buffer ten minste één frame met echte gegevens en kan deze net zo veel als een halve seconde echte gegevens bevatten. De rest van de buffer bevat stilte. De Slaapstand aanroep die de lus volgt, biedt voldoende tijd (een halve seconde) om alle resterende gegevens af te spelen. De stilte die volgt op de gegevens voorkomt ongewenste geluiden voordat de oproep naar de IAudioClient::Stop methode de audiostream stopt. Zie de Windows SDK-documentatie voor meer informatie over Sleep.

Na de aanroep van de IAudioClient::Initialize methode, blijft de stream open totdat de client alle verwijzingen naar de IAudioClient interface en naar alle verwijzingen naar service-interfaces die de client via de IAudioClient::GetService methode heeft verkregen, vrijgeeft. De laatste Release aanroep sluit de stream.

Met de functie PlayAudioStream in het voorgaande codevoorbeeld wordt de functie CoCreateInstance aangeroepen om een opsomming te maken voor de audio-eindpuntapparaten in het systeem. Tenzij het aanroepende programma eerder de CoCreateInstance of CoInitializeEx functie heeft aangeroepen om de COM-bibliotheek te initialiseren, mislukt de CoCreateInstance aanroep. Zie de Windows SDK-documentatie voor meer informatie over CoCreateInstance, CoCreateInstanceen CoInitializeEx.

Stream Management-