Exclusive-modusstromen

Zoals eerder uitgelegd, heeft de toepassing, als een toepassing een stream opent in de exclusieve modus, exclusief gebruik van het audio-eindpuntapparaat dat de stream afspeelt of registreert. Verschillende toepassingen kunnen daarentegen een audio-eindpuntapparaat delen door streams in de gedeelde modus op het apparaat te openen.

Tip

Vanaf Windows 10 maakt IAudioClient3gedeelde-modusstreams mogelijk met zeer lage periodegrootten (zo laag als de waarde die wordt geretourneerd door GetSharedModeEnginePeriod). Dit zorgt voor latentie die vergelijkbaar is met de exclusieve modus, terwijl systeemgeluiden en interoperabiliteit behouden blijven. Evalueer streams met een korte periode in gedeelde modus voordat u voor de exclusieve modus kiest, tenzij u een strikte vereiste hebt voor bit-exacte uitvoer of aangepaste samplefrequenties.

Warning

Exclusieve-modusstreams grijpen het audio-eindpunt in: alle systeemgeluiden, meldingen en audio van andere toepassingen worden stilgestaan. Hoewel exclusieve toegang standaard is ingeschakeld in de geluidsinstellingen, kunnen gebruikers deze per apparaat uitschakelen. Geef altijd een door de gebruiker zichtbare optie om terug te vallen op de gedeelde modus en laat het apparaat onmiddellijk los wanneer uw toepassing de focus verliest.

Exclusieve toegang tot een audioapparaat kan cruciale systeemgeluiden blokkeren, interoperabiliteit met andere toepassingen voorkomen en anders de gebruikerservaring verminderen. Om deze problemen te beperken, geeft een toepassing met een stroom in exclusieve modus de controle over het audioapparaat doorgaans op wanneer de toepassing niet het actieve voorgrondproces is of niet actief audio streamt.

Streamlatentie is de vertraging die inherent is aan het gegevenspad dat de eindpuntbuffer van een toepassing verbindt met een audio-eindpuntapparaat. Voor een renderingstream is de latentie de maximale vertraging vanaf het moment dat een toepassing een voorbeeld naar een eindpuntbuffer schrijft tot het moment dat het voorbeeld via de luidsprekers wordt gehoord. Voor een opnamestroom is de latentie de maximale vertraging vanaf het moment dat een geluid de microfoon binnenkomt tot het moment dat een toepassing het voorbeeld voor dat geluid kan lezen vanuit de eindpuntbuffer.

Toepassingen die gebruikmaken van exclusieve modusstreams doen dit vaak omdat ze lage latenties in de gegevenspaden tussen de audio-eindpuntapparaten en de toepassingsthreads vereisen die toegang hebben tot de eindpuntbuffers. Doorgaans draaien deze threads met een relatief hoge prioriteit en worden ze zo ingepland dat ze worden uitgevoerd met periodieke intervallen die dicht liggen bij of gelijk zijn aan het periodieke interval tussen opeenvolgende verwerkingscycli van de audiohardware. Tijdens elke pass verwerkt de audiohardware de nieuwe gegevens in de eindpuntbuffers.

Voor het bereiken van de kleinste stroomlatenties vereist een toepassing mogelijk zowel speciale audiohardware als een computersysteem dat licht wordt geladen. De audiohardware tot buiten de timinglimieten aansturen of het systeem belasten met andere taken met hoge prioriteit kan leiden tot een verstoring in een audiostream met lage latentie. Als voorbeeld geldt dat bij een renderingstream een glitch kan optreden als de toepassing er niet in slaagt naar een endpointbuffer te schrijven voordat de audiohardware de buffer leest, of als de hardware de buffer niet leest vóór het tijdstip waarop de buffer moet worden afgespeeld. Normaal gesproken moet een toepassing die is bedoeld om te worden uitgevoerd op een breed scala aan audiohardware en in een breed scala aan systemen de timingvereisten versoepelen om glitches in alle doelomgevingen te voorkomen.

Windows Vista biedt verschillende functies ter ondersteuning van toepassingen die audiostreams met lage latentie vereisen. Zoals besproken in User-Mode audioonderdelen, kunnen toepassingen die tijdkritieke bewerkingen uitvoeren de MMCSS-functies (Multimedia Class Scheduler Service) aanroepen om de threadprioriteit te verhogen zonder CPU-resources te weigeren voor toepassingen met een lagere prioriteit. Daarnaast ondersteunt de methode IAudioClient::Initialize een AUDCLNT_STREAMFLAGS_EVENTCALLBACK vlag waarmee de bufferservicethread van een toepassing de uitvoering kan plannen wanneer er een nieuwe buffer beschikbaar is vanaf het audioapparaat. Door deze functies te gebruiken, kan een toepassingsthread onzekerheid verminderen over wanneer deze wordt uitgevoerd, waardoor het risico op glitches in een audiostroom met lage latentie wordt verminderd.

De stuurprogramma's voor oudere audioadapters gebruiken waarschijnlijk de WaveCyclic- of WavePci-apparaatstuurprogrammainterface (DDI), terwijl de stuurprogramma's voor nieuwere audioadapters waarschijnlijk de WaveRT DDI ondersteunen. Voor exclusieve toepassingen kunnen WaveRT-stuurprogramma's betere prestaties bieden dan WaveCyclic- of WavePci-stuurprogramma's, maar WaveRT-stuurprogramma's vereisen extra hardwaremogelijkheden. Deze mogelijkheden omvatten de mogelijkheid om hardwarebuffers rechtstreeks te delen met toepassingen. Bij direct delen is er geen systeemingreep vereist om gegevens over te dragen tussen een exclusieve toepassing en de audiohardware. Daarentegen zijn WaveCyclic- en WavePci-stuurprogramma's geschikt voor oudere, minder geschikte audioadapters. Deze adapters zijn afhankelijk van systeemsoftware voor het transport van gegevensblokken (gekoppeld aan systeem-I/O-aanvraagpakketten of IRP's) tussen toepassingsbuffers en hardwarebuffers. Bovendien zijn USB-audioapparaten afhankelijk van systeemsoftware voor het transport van gegevens tussen toepassingsbuffers en hardwarebuffers. Ter verbetering van de prestaties van exclusieve-modustoepassingen die verbinding maken met audioapparaten die afhankelijk zijn van het systeem voor gegevenstransport, verhoogt WASAPI automatisch de prioriteit van de systeemthreads die gegevens overdragen tussen de toepassingen en de hardware. WASAPI maakt gebruik van MMCSS om de prioriteit van de thread te verhogen. Als in Windows Vista een systeemthread gegevenstransport beheert voor een exclusieve audio-afspeelstroom met een PCM-indeling en een apparaatperiode van minder dan 10 milliseconden, wijst WASAPI de mmCSS-taaknaam Pro Audio toe aan de thread. Als de apparaatperiode van de stream groter is dan of gelijk is aan 10 milliseconden, wijst WASAPI de MMCSS-taaknaam 'Audio' toe aan de thread. Zie de Windows DDK-documentatie voor meer informatie over de WaveCyclic-, WavePci- en WaveRT-DDK-DDIS's. Zie IAudioClient::GetDevicePeriodvoor informatie over het selecteren van een geschikte apparaatperiode.

Zoals beschreven in Session Volume Controls, biedt WASAPI de ISimpleAudioVolume, IChannelAudioVolumeen IAudioStreamVolume interfaces voor het beheren van de volumeniveaus van audiostreams in de gedeelde modus. De besturingselementen in deze interfaces hebben echter geen invloed op streams in de exclusieve modus. In plaats daarvan gebruiken toepassingen die exclusieve modusstreams beheren doorgaans de interface IAudioEndpointVolume in de EndpointVolume-API om de volumeniveaus van deze streams te beheren. Zie Endpoint Volume Controlsvoor meer informatie over deze interface.

Voor elk afspeelapparaat en opnameapparaat in het systeem kan de gebruiker bepalen of het apparaat kan worden gebruikt in de exclusieve modus. Als de gebruiker exclusief gebruik van het apparaat uitschakelt, kan het apparaat worden gebruikt om alleen gedeelde modusstreams af te spelen of op te nemen.

Als de gebruiker exclusief gebruik van het apparaat inschakelt, kan de gebruiker ook bepalen of een aanvraag van een toepassing voor het gebruik van het apparaat in de exclusieve modus het gebruik van het apparaat voorkomt door toepassingen die momenteel gedeelde modusstreams afspelen of opnemen via het apparaat. Als voorrang is ingeschakeld, slaagt een aanvraag van een toepassing om exclusief beheer van het apparaat te nemen als het apparaat momenteel niet in gebruik is of als het apparaat wordt gebruikt in de gedeelde modus, maar de aanvraag mislukt als een andere toepassing al exclusief beheer van het apparaat heeft. Als voorrang is uitgeschakeld, slaagt een aanvraag van een toepassing om exclusief beheer van het apparaat uit te voeren als het apparaat momenteel niet in gebruik is, maar de aanvraag mislukt als het apparaat al wordt gebruikt in de gedeelde modus of in de exclusieve modus.

In Windows Vista zijn de standaardinstellingen voor een audio-eindpuntapparaat het volgende:

  • Het apparaat kan worden gebruikt om exclusieve modusstreams af te spelen of op te nemen.
  • Een verzoek om een apparaat te gebruiken voor het afspelen of opnemen van een stream in exclusieve modus heeft voorrang op elke stream in gedeelde modus die momenteel via het apparaat wordt afgespeeld of opgenomen.

De exclusieve modusinstellingen van een afspeel- of opnameapparaat wijzigen

  1. Klik met de rechtermuisknop op het luidsprekerpictogram in het systeemvak, dat zich aan de rechterkant van de taakbalk bevindt en selecteer Afspeelapparaten of Opnameapparaten. (Als alternatief voert u het Configuratiescherm van Windows multimedia uit, Mmsys.cpl, vanuit een opdrachtpromptvenster. Zie Opmerkingen in DEVICE_STATE_XXX Constantenvoor meer informatie.)
  2. Nadat het venster Geluid wordt weergegeven, selecteert u Afspelen of Opnemen. Selecteer vervolgens een vermelding in de lijst met apparaatnamen en klik op Eigenschappen.
  3. Nadat het venster Eigenschappen wordt weergegeven, klikt u op Geavanceerde.
  4. Als u wilt dat toepassingen het apparaat in de exclusieve modus kunnen gebruiken, schakelt u het selectievakje Toestaan dat toepassingen exclusieve controle over dit apparaat overnemen. Als u het gebruik van de exclusieve modus van het apparaat wilt uitschakelen, schakelt u het selectievakje uit.
  5. Als het gebruik van de exclusieve modus van het apparaat is ingeschakeld, kunt u opgeven of een aanvraag voor exclusief beheer van het apparaat slaagt als het apparaat momenteel gedeelde modusstreams afspeelt of opneemt. Als u toepassingen in de exclusieve modus prioriteit wilt geven boven toepassingen in de gedeelde modus, schakelt u het selectievakje in Exclusieve modustoepassingen prioriteit geven. Als u de prioriteit van toepassingen voor exclusieve modus wilt weigeren ten opzichte van toepassingen in de gedeelde modus, schakelt u het selectievakje uit.

In het volgende codevoorbeeld ziet u hoe u een audiostream met lage latentie afspeelt op een audiorenderingsapparaat dat is geconfigureerd voor gebruik in de exclusieve modus:

//-----------------------------------------------------------
// Play an exclusive-mode stream on the default audio
// rendering device. The PlayExclusiveStream function uses
// event-driven buffering and MMCSS to play the stream at
// the minimum latency supported by the device.
//-----------------------------------------------------------

// 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 PlayExclusiveStream(MyAudioSource *pMySource)
{
    HRESULT hr;
    REFERENCE_TIME hnsRequestedDuration = 0;
    IMMDeviceEnumerator *pEnumerator = NULL;
    IMMDevice *pDevice = NULL;
    IAudioClient *pAudioClient = NULL;
    IAudioRenderClient *pRenderClient = NULL;
    WAVEFORMATEX *pwfx = NULL;
    HANDLE hEvent = NULL;
    HANDLE hTask = NULL;
    UINT32 bufferFrameCount;
    BYTE *pData;
    DWORD flags = 0;
    DWORD taskIndex = 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)

    // Call a helper function to negotiate with the audio
    // device for an exclusive-mode stream format.
    hr = GetStreamFormat(pAudioClient, &pwfx);
    EXIT_ON_ERROR(hr)

    // Initialize the stream to play at the minimum latency.
    hr = pAudioClient->GetDevicePeriod(NULL, &hnsRequestedDuration);
    EXIT_ON_ERROR(hr)

    hr = pAudioClient->Initialize(
                         AUDCLNT_SHAREMODE_EXCLUSIVE,
                         AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
                         hnsRequestedDuration,
                         hnsRequestedDuration,
                         pwfx,
                         NULL);
    if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
        // Align the buffer if needed, see IAudioClient::Initialize() documentation
        UINT32 nFrames = 0;
        hr = pAudioClient->GetBufferSize(&nFrames);
        EXIT_ON_ERROR(hr)
        hnsRequestedDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC / pwfx->nSamplesPerSec * nFrames + 0.5);
        hr = pAudioClient->Initialize(
            AUDCLNT_SHAREMODE_EXCLUSIVE,
            AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
            hnsRequestedDuration,
            hnsRequestedDuration,
            pwfx,
            NULL);
    }
    EXIT_ON_ERROR(hr)

    // Tell the audio source which format to use.
    hr = pMySource->SetFormat(pwfx);
    EXIT_ON_ERROR(hr)

    // Create an event handle and register it for
    // buffer-event notifications.
    hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (hEvent == NULL)
    {
        hr = E_FAIL;
        goto Exit;
    }

    hr = pAudioClient->SetEventHandle(hEvent);
    EXIT_ON_ERROR(hr);

    // Get the actual size of the two allocated buffers.
    hr = pAudioClient->GetBufferSize(&bufferFrameCount);
    EXIT_ON_ERROR(hr)

    hr = pAudioClient->GetService(
                         IID_IAudioRenderClient,
                         (void**)&pRenderClient);
    EXIT_ON_ERROR(hr)

    // To reduce latency, load the first buffer with data
    // from the audio source before starting the stream.
    hr = pRenderClient->GetBuffer(bufferFrameCount, &pData);
    EXIT_ON_ERROR(hr)

    hr = pMySource->LoadData(bufferFrameCount, pData, &flags);
    EXIT_ON_ERROR(hr)

    hr = pRenderClient->ReleaseBuffer(bufferFrameCount, flags);
    EXIT_ON_ERROR(hr)

    // Ask MMCSS to temporarily boost the thread priority
    // to reduce glitches while the low-latency stream plays.
    hTask = AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskIndex);
    if (hTask == NULL)
    {
        hr = E_FAIL;
        EXIT_ON_ERROR(hr)
    }

    hr = pAudioClient->Start();  // Start playing.
    EXIT_ON_ERROR(hr)

    // Each loop fills one of the two buffers.
    while (flags != AUDCLNT_BUFFERFLAGS_SILENT)
    {
        // Wait for next buffer event to be signaled.
        DWORD retval = WaitForSingleObject(hEvent, 2000);
        if (retval != WAIT_OBJECT_0)
        {
            // Event handle timed out after a 2-second wait.
            pAudioClient->Stop();
            hr = ERROR_TIMEOUT;
            goto Exit;
        }

        // Grab the next empty buffer from the audio device.
        hr = pRenderClient->GetBuffer(bufferFrameCount, &pData);
        EXIT_ON_ERROR(hr)

        // Load the buffer with data from the audio source.
        hr = pMySource->LoadData(bufferFrameCount, pData, &flags);
        EXIT_ON_ERROR(hr)

        hr = pRenderClient->ReleaseBuffer(bufferFrameCount, flags);
        EXIT_ON_ERROR(hr)
    }

    // Wait for the last buffer to play before stopping.
    Sleep((DWORD)(hnsRequestedDuration/REFTIMES_PER_MILLISEC));

    hr = pAudioClient->Stop();  // Stop playing.
    EXIT_ON_ERROR(hr)

Exit:
    if (hEvent != NULL)
    {
        CloseHandle(hEvent);
    }
    if (hTask != NULL)
    {
        AvRevertMmThreadCharacteristics(hTask);
    }
    CoTaskMemFree(pwfx);
    SAFE_RELEASE(pEnumerator)
    SAFE_RELEASE(pDevice)
    SAFE_RELEASE(pAudioClient)
    SAFE_RELEASE(pRenderClient)

    return hr;
}

In het voorgaande codevoorbeeld wordt de functie PlayExclusiveStream uitgevoerd in de toepassingsthread die het eindpunt buffert terwijl een renderingstream wordt afgespeeld. De functie heeft één parameter, pMySource, een aanwijzer naar een object dat deel uitmaakt van een door de client gedefinieerde klasse MyAudioSource. Deze klasse heeft twee lidfuncties, LoadData en SetFormat, die worden aangeroepen in het codevoorbeeld. MyAudioSource wordt beschreven in Rendering a Stream.

Met de functie PlayExclusiveStream wordt een helperfunctie aangeroepen, GetStreamFormat, die onderhandelt met het standaard renderingapparaat om te bepalen of het apparaat een streamindeling met exclusieve modus ondersteunt die geschikt is voor gebruik door de toepassing. De code voor de functie GetStreamFormat wordt niet weergegeven in het codevoorbeeld; dat komt doordat de details van de implementatie volledig afhankelijk zijn van de vereisten van de toepassing. De werking van de functie GetStreamFormat kan echter eenvoudig worden beschreven. Hiermee wordt de methode IAudioClient::IsFormatSupported methode een of meer keren aangeroepen om te bepalen of het apparaat een geschikte indeling ondersteunt. De vereisten van de toepassing bepalen welke indelingen GetStreamFormat aan de methode IsFormatSupported voorlegt en in welke volgorde het deze voorlegt. Zie Device Formatsvoor meer informatie over IsFormatSupported.

Na de aanroep GetStreamFormat roept de functie PlayExclusiveStream de IAudioClient::GetDevicePeriod methode aan om de minimale apparaatperiode te verkrijgen die wordt ondersteund door de audiohardware. Vervolgens roept de functie de IAudioClient::Initialiseer methode om een bufferduur aan te vragen die gelijk is aan de minimale periode. Als de aanroep slaagt, wijst de methode Initialize twee eindpuntbuffers toe, elk met een duur gelijk aan de minimumperiode. Later, wanneer de audiostream begint te lopen, delen de toepassing en de audiohardware de twee buffers volgens het 'pingpong'-principe; dat wil zeggen: terwijl de toepassing naar de ene buffer schrijft, leest de hardware uit de andere buffer.

Voordat u de stream start, doet de functie PlayExclusiveStream het volgende:

  • Hiermee maakt en registreert u de gebeurtenisgreep waarmee deze meldingen ontvangt wanneer buffers klaar zijn om te worden ingevuld.
  • Vult de eerste buffer met gegevens van de audiobron om de vertraging te verminderen van wanneer de stream wordt uitgevoerd wanneer het eerste geluid wordt gehoord.
  • Roept de AvSetMmThreadCharacteristics functie aan om aan te vragen dat MMCSS de prioriteit van de thread verhoogt waarin PlayExclusiveStream wordt uitgevoerd. (Wanneer de stream niet meer wordt uitgevoerd, herstelt de AvRevertMmThreadCharacteristics functieaanroep de oorspronkelijke threadprioriteit.)

Zie de Windows SDK-documentatie voor meer informatie over AvSetMmThreadCharacteristics en AvRevertMmThreadCharacteristics.

Terwijl de stream actief is, vult elke iteratie van de while-lus in het voorgaande codevoorbeeld één endpointbuffer. Tussen iteraties wacht de WaitForSingleObject functieaanroep totdat de gebeurtenisgreep wordt gesignaleerd. Wanneer de handle een signaal krijgt, doet de lusinhoud het volgende:

  1. Roept de methode IAudioRenderClient::GetBuffer aan om de volgende buffer op te halen.
  2. Vult de buffer.
  3. Roept de IAudioRenderClient::ReleaseBuffer methode aan om de buffer vrij te geven.

Zie de Windows SDK-documentatie voor meer informatie over WaitForSingleObject.

Als de audioadapter wordt beheerd door een WaveRT-stuurprogramma, wordt de signalering van de gebeurtenisgreep gekoppeld aan de DMA-overdrachtsmeldingen van de audiohardware. Voor een USB-audioapparaat of voor een audioapparaat dat wordt beheerd door een WaveCyclic- of WavePci-stuurprogramma, is de signalering van de gebeurtenisgreep gekoppeld aan voltooiingen van de IRP's die gegevens overdragen van de toepassingsbuffer naar de hardwarebuffer.

In het voorgaande codevoorbeeld worden de audiohardware en het computersysteem naar hun prestatielimieten gepusht. Ten eerste stelt de toepassing de thread voor bufferverwerking zo in dat deze het minimale apparaatinterval gebruikt dat door de audiohardware wordt ondersteund, om de streamlatentie te verminderen. Ten tweede, om ervoor te zorgen dat de thread betrouwbaar wordt uitgevoerd binnen elke apparaatperiode, stelt de AvSetMmThreadCharacteristics functie-aanroep de parameter TaskName in op 'Pro Audio', wat in Windows Vista de standaardtaaknaam met de hoogste prioriteit is. Overweeg of de timingvereisten van uw toepassing mogelijk worden versoepeld zonder de bruikbaarheid ervan in gevaar te brengen. De toepassing kan bijvoorbeeld plannen dat de bufferservicethread een periode gebruikt die langer is dan het minimum. Een langere periode kan het gebruik van een lagere threadprioriteit veilig toestaan.

Stream Management-