Compartilhar via


Áudio de baixa latência

Este artigo discute as alterações de latência de áudio no Windows 10. Ele aborda opções de API para desenvolvedores de aplicativos e alterações em drivers que podem ser feitas para dar suporte a áudio de baixa latência. Latência de áudio é o atraso entre esse tempo em que o som é criado e quando ele é ouvido. Ter baixa latência de áudio é importante para vários cenários principais, como:

  • Áudio pro
  • Criação de música
  • Comunicações
  • Realidade virtual
  • Jogos

As metas deste documento são:

  1. Descrever as fontes de latência de áudio no Windows.
  2. Explique as alterações que reduzem a latência de áudio na pilha de áudio Windows 10.
  3. Forneça uma referência sobre como desenvolvedores de aplicativos e fabricantes de hardware podem aproveitar a nova infraestrutura, a fim de desenvolver aplicativos e drivers com baixa latência de áudio.

Este artigo cobre:

  1. A nova API do AudioGraph para cenários interativos e de criação de mídia.
  2. Alterações no WASAPI para dar suporte à baixa latência.
  3. Aprimoramentos nos DDIs do driver.

Terminologia

Termo Descrição
Latência de renderização Atraso entre o tempo em que um aplicativo envia um buffer de dados de áudio para as APIs de renderização até o momento em que ele é ouvido dos alto-falantes.
Capturar latência Atraso entre o tempo em que um som é capturado do microfone até o momento em que ele é enviado para as APIs de captura que estão sendo usadas pelo aplicativo.
Latência de ida e volta Atraso entre o tempo em que um som é capturado do microfone, processado pelo aplicativo e enviado pelo aplicativo para renderização para os alto-falantes. É aproximadamente igual a latência de renderização + latência de captura.
Latência de toque para aplicativo Atraso entre o tempo em que um usuário toca na tela até o momento em que o sinal é enviado para o aplicativo.
Latência de toque para som Atraso entre o tempo em que um usuário toca na tela, o evento vai para o aplicativo e um som é ouvido por meio dos alto-falantes. É igual a renderizar latência + latência de toque para aplicativo.

Pilha de áudio do Windows

O diagrama a seguir mostra uma versão simplificada da pilha de áudio do Windows.

Diagrama mostrando a pilha de áudio de baixa latência com aplicativos, driver de mecanismo de áudio e hardware.

Aqui está um resumo das latências no caminho de renderização: objetos de processamento de áudio

  1. O aplicativo grava os dados em um buffer

  2. O mecanismo de áudio lê os dados do buffer e os processa. Ele também carrega efeitos de áudio na forma de APOs (objetos de processamento de áudio). Para obter mais informações sobre APOs, consulte Objetos de processamento de áudio do Windows.

  3. A latência das APOs varia de acordo com o processamento de sinal dentro das APOs.

  4. Antes de Windows 10, a latência do mecanismo de áudio era igual a ~12 ms para aplicativos que usam dados de ponto flutuante e ~6 ms para aplicativos que usam dados inteiros

  5. Em Windows 10 e posterior, a latência foi reduzida para 1,3 ms para todos os aplicativos

  6. O mecanismo de áudio grava os dados processados em um buffer.

  7. Antes de Windows 10, o buffer sempre era definido como ~10 ms.

  8. Começando com Windows 10, o tamanho do buffer é definido pelo driver de áudio (mais detalhes sobre o buffer são descritos posteriormente neste artigo).

  9. O driver de áudio lê os dados do buffer e os grava no hardware.

  10. O hardware também pode processar os dados novamente na forma de mais efeitos de áudio.

  11. O usuário ouve o áudio do alto-falante.

Aqui está um resumo da latência no caminho de captura:

  1. O áudio é capturado do microfone.

  2. O hardware pode processar os dados. Por exemplo, para adicionar efeitos de áudio.

  3. O driver lê os dados do hardware e grava os dados em um buffer.

  4. Antes de Windows 10, esse buffer sempre era definido como 10 ms.

  5. Começando com Windows 10, o tamanho do buffer é definido pelo driver de áudio (mais detalhes abaixo).

  6. O mecanismo de áudio lê os dados do buffer e os processa. Ele também carrega efeitos de áudio na forma de APOs (objetos de processamento de áudio).

  7. A latência das APOs varia de acordo com o processamento de sinal dentro das APOs.

  8. Antes de Windows 10, a latência do mecanismo de áudio era igual a ~6 ms para aplicativos que usam dados de ponto flutuante e ~0ms para aplicativos que usam dados inteiros.

  9. Em Windows 10 e posterior, a latência foi reduzida para ~0ms para todos os aplicativos.

  10. O aplicativo é sinalizado de que os dados estão disponíveis para leitura, assim que o mecanismo de áudio termina com seu processamento. A pilha de áudio também fornece a opção de modo exclusivo. Nesse caso, os dados ignoram o mecanismo de áudio e vão diretamente do aplicativo para o buffer do qual o driver os lê. No entanto, se um aplicativo abrir um ponto de extremidade no modo exclusivo, não haverá nenhum outro aplicativo que possa usar esse ponto de extremidade para renderizar ou capturar áudio.

Outra alternativa popular para aplicativos que precisam de baixa latência é usar o modelo ASIO (Entrada/Saída de Fluxo de Áudio), que utiliza o modo exclusivo. Depois que um usuário instala um driver ASIO de terceiros, os aplicativos podem enviar dados diretamente do aplicativo para o driver ASIO. No entanto, o aplicativo precisa ser escrito de forma que ele fale diretamente com o driver asio.

Ambas as alternativas (modo exclusivo e ASIO) têm suas próprias limitações. Eles fornecem baixa latência, mas têm suas próprias limitações (algumas das quais foram descritas acima). Como resultado, o mecanismo de áudio foi modificado para reduzir a latência, mantendo a flexibilidade.

Melhorias na pilha de áudio

Windows 10 e posterior foram aprimorados em três áreas para reduzir a latência:

  1. Todos os aplicativos que usam áudio verão uma redução de 4,5-16 ms na latência de ida e volta (como foi explicado na seção acima) sem nenhuma alteração de código ou atualizações de driver, em comparação com Windows 8.1.
    1. Os aplicativos que usam dados de ponto flutuante terão latência inferior de 16 ms.
    2. Os aplicativos que usam dados inteiros terão latência inferior de 4,5 ms.
  2. Os sistemas com drivers atualizados fornecerão latência de ida e volta ainda menor:
    1. Os drivers podem usar novos DDIs para relatar os tamanhos com suporte do buffer usado para transferir dados entre o Windows e o hardware. As transferências de dados nem sempre precisam usar buffers de 10 ms, como fizeram em versões anteriores do Windows. Em vez disso, o driver pode especificar se pode usar buffers pequenos, por exemplo, 5 ms, 3 ms, 1 ms etc.
    2. Aplicativos que exigem baixa latência podem usar novas APIs de áudio (AudioGraph ou WASAPI), para consultar os tamanhos de buffer compatíveis com o driver e selecionar aquele que será usado para a transferência de dados de/para o hardware.
  3. Quando um aplicativo usa tamanhos de buffer abaixo de um determinado limite para renderizar e capturar áudio, o Windows entra em um modo especial, em que gerencia seus recursos de uma maneira que evita a interferência entre o streaming de áudio e outros subsistemas. Isso reduzirá as interrupções na execução do subsistema de áudio e minimizará a probabilidade de falhas de áudio. Quando o aplicativo para de transmitir, o Windows retorna ao modo de execução normal. O subsistema de áudio consiste nos seguintes recursos:
    1. O thread do mecanismo de áudio que está processando áudio de baixa latência.
    2. Todos os threads e interrupções que foram registrados pelo driver (usando os novos DDIs descritos na seção sobre o registro de recursos do driver).
    3. Alguns ou todos os threads de áudio dos aplicativos que solicitam buffers pequenos e de todos os aplicativos que compartilham o mesmo grafo de dispositivo de áudio (por exemplo, o mesmo modo de processamento de sinal) com qualquer aplicativo que solicitou buffers pequenos:
  4. Retornos de chamada do AudioGraph no caminho de streaming.
  5. Se o aplicativo usar WASAPI, somente os itens de trabalho que foram enviados para a API de Fila de Trabalho em Tempo Real ou MFCreateMFByteStreamOnStreamEx e foram marcados como "Áudio" ou "ProAudio".

Melhorias na API

As duas APIs de Windows 10 a seguir fornecem recursos de baixa latência:

Para determinar quais das duas APIs usar:

  • Favorecimento ao AudioGraph, sempre que possível para o desenvolvimento de novos aplicativos.
  • Use WASAPI somente se:
    • Você precisa de mais controle do que o fornecido pelo AudioGraph.
    • Você precisa de latência menor do que a fornecida pelo AudioGraph.

A seção ferramentas de medida deste artigo mostra medidas específicas de um sistema Haswell usando o driver HDAudio da caixa de entrada.

As seções a seguir explicarão os recursos de baixa latência em cada API. Como foi observado na seção anterior, para que o sistema obtenha a latência mínima, ele precisa ter drivers atualizados que dão suporte a tamanhos de buffer pequenos.

AudioGraph

O AudioGraph é uma nova API de Plataforma Universal do Windows no Windows 10 e posterior que visa realizar cenários de criação de música e interativa com facilidade. O AudioGraph está disponível em várias linguagens de programação (C++, C#, JavaScript) e tem um modelo de programação simples e rico em recursos.

Para direcionar cenários de baixa latência, o AudioGraph fornece a propriedade AudioGraphSettings::QuantumSizeSelectionMode . Essa propriedade pode ser qualquer um dos valores mostrados na tabela abaixo:

Valor Descrição
SystemDefault Define o buffer como o tamanho do buffer padrão (~10 ms)
LowerLatency Define o buffer como o valor mínimo compatível com o driver
ClosestToDesired Define o tamanho do buffer como igual ao valor definido pela propriedade DesiredSamplesPerQuantum ou a um valor tão próximo de DesiredSamplesPerQuantum quanto tem suporte do driver.

O exemplo AudioCreation mostra como usar AudioGraph para baixa latência. O snippet de código a seguir mostra como definir o tamanho mínimo do buffer:

AudioGraphSettings settings = new AudioGraphSettings(AudioRenderCategory.Media);
settings.QuantumSizeSelectionMode = QuantumSizeSelectionMode.LowestLatency;
CreateAudioGraphResult result = await AudioGraph.CreateAsync(settings);

WASAPI (API de sessão de áudio) do Windows

A partir do Windows 10, o WASAPI foi aprimorado para:

  • Permitir que um aplicativo descubra o intervalo de tamanhos de buffer (ou seja, valores de periodicidade) compatíveis com o driver de áudio de um determinado dispositivo de áudio. Isso possibilita que um aplicativo escolha entre o tamanho do buffer padrão (10 ms) ou um buffer pequeno (menos de 10 ms) ao abrir um fluxo no modo compartilhado. Se um aplicativo não especificar um tamanho de buffer, ele usará o tamanho do buffer padrão.
  • Permitir que um aplicativo descubra o formato atual e a periodicidade do mecanismo de áudio. Isso permite que os aplicativos se ajustem às configurações atuais do mecanismo de áudio.
  • Permitir que um aplicativo especifique que deseja renderizar/capturar no formato especificado sem qualquer resampling pelo mecanismo de áudio

Os recursos acima estarão disponíveis em todos os dispositivos Windows. No entanto, determinados dispositivos com recursos suficientes e drivers atualizados fornecerão uma experiência de usuário melhor do que outros.

A funcionalidade acima é fornecida por uma nova interface, chamada IAudioClient3, que deriva de IAudioClient2.

IAudioClient3 define os três métodos a seguir:

Método Descrição
GetCurrentSharedModeEnginePeriod Retorna o formato atual e a periodicidade do mecanismo de áudio
GetSharedModeEnginePeriod Retorna o intervalo de periodicidades com suporte pelo mecanismo para o formato de fluxo especificado
InitializeSharedAudioStream Inicializa um fluxo compartilhado com a periodicidade especificada

O exemplo WASAPIAudio mostra como usar IAudioClient3 para baixa latência.

O snippet de código a seguir mostra como um aplicativo de criação de música pode operar na configuração de latência mais baixa compatível com o sistema.

// 1. Activation

// Get a string representing the Default Audio (Render|Capture) Device
m_DeviceIdString = MediaDevice::GetDefaultAudio(Render|Capture)Id(
Windows::Media::Devices::AudioDeviceRole::Default );

// This call must be made on the main UI thread.  Async operation will call back to
// IActivateAudioInterfaceCompletionHandler::ActivateCompleted, which must be an agile // interface implementation
hr = ActivateAudioInterfaceAsync( m_DeviceIdString->Data(), __uuidof(IAudioClient3),
nullptr, this, &asyncOp );

// 2. Setting the audio client properties – note that low latency offload is not supported

AudioClientProperties audioProps = {0};
audioProps.cbSize = sizeof( AudioClientProperties );
audioProps.eCategory = AudioCategory_Media;

// if the device has System.Devices.AudioDevice.RawProcessingSupported set to true and you want to use raw mode
// audioProps.Options |= AUDCLNT_STREAMOPTIONS_RAW;
//
// if it is important to avoid resampling in the audio engine, set this flag
// audioProps.Options |= AUDCLNT_STREAMOPTIONS_MATCH_FORMAT;


hr = m_AudioClient->SetClientProperties( &audioProps ); if (FAILED(hr)) { ... }

// 3. Querying the legal periods

hr = m_AudioClient->GetMixFormat( &mixFormat ); if (FAILED(hr)) { ... }

hr = m_AudioClient->GetSharedModeEnginePeriod(wfx, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); if (FAILED(hr)) { ... }

// legal periods are any multiple of fundamentalPeriodInFrames between
// minPeriodInFrames and maxPeriodInFrames, inclusive
// the Windows shared-mode engine uses defaultPeriodInFrames unless an audio client // has specifically requested otherwise

// 4. Initializing a low-latency client

hr = m_AudioClient->InitializeSharedAudioStream(
         AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
         desiredPeriodInFrames,
         mixFormat,
         nullptr); // audio session GUID
         if (AUDCLNT_E_ENGINE_PERIODICITY_LOCKED == hr) {
         /* engine is already running at a different period; call m_AudioClient->GetSharedModeEnginePeriod to see what it is */
         } else if (FAILED(hr)) {
             ...
         }

// 5. Initializing a client with a specific format (if the format needs to be different than the default format)

AudioClientProperties audioProps = {0};
audioProps.cbSize = sizeof( AudioClientProperties );
audioProps.eCategory = AudioCategory_Media;
audioProps.Options |= AUDCLNT_STREAMOPTIONS_MATCH_FORMAT;

hr = m_AudioClient->SetClientProperties( &audioProps );
if (FAILED(hr)) { ... }

hr = m_AudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, appFormat, &closest);
if (S_OK == hr) {
       /* device supports the app format */
} else if (S_FALSE == hr) {
       /* device DOES NOT support the app format; closest supported format is in the "closest" output variable */
} else {
       /* device DOES NOT support the app format, and Windows could not find a close supported format */
}

hr = m_AudioClient->InitializeSharedAudioStream(
       AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
       defaultPeriodInFrames,
       appFormat,
       nullptr); // audio session GUID
if (AUDCLNT_E_ENGINE_FORMAT_LOCKED == hr) {
       /* engine is already running at a different format */
} else if (FAILED(hr)) {
       ...
}

Além disso, a Microsoft recomenda que os aplicativos que usam WASAPI também usem a API de Fila de Trabalho em Tempo Real ou o MFCreateMFByteStreamOnStreamEx para criar itens de trabalho e marcá-los como Áudio ou Áudio Pro, em vez de seus próprios threads. Isso permitirá que o Windows os gerencie de uma maneira que evite a interferência de subsistemas que não sejam de áudio. Por outro lado, todos os threads do AudioGraph são gerenciados automaticamente corretamente pelo Windows. O snippet de código a seguir do exemplo WASAPIAudio mostra como usar as APIs da Fila de Trabalho do MF.

// Specify Source Reader Attributes
Attributes->SetUnknown( MF_SOURCE_READER_ASYNC_CALLBACK, static_cast<IMFSourceReaderCallback *>(this) );
    if (FAILED( hr ))
    {
        goto exit;
    }
    Attributes->SetString( MF_READWRITE_MMCSS_CLASS_AUDIO, L"Audio" );
    if (FAILED( hr ))
    {
        goto exit;
    }
    Attributes->SetUINT32( MF_READWRITE_MMCSS_PRIORITY_AUDIO, 0 );
    if (FAILED( hr ))
    {
        goto exit;
    }
    // Create a stream from IRandomAccessStream
    hr = MFCreateMFByteStreamOnStreamEx (reinterpret_cast<IUnknown*>(m_ContentStream), &ByteStream );
    if ( FAILED( hr ) )
    {
        goto exit;
    }
    // Create source reader
    hr = MFCreateSourceReaderFromByteStream( ByteStream, Attributes, &m_MFSourceReader );

Como alternativa, o snippet de código a seguir mostra como usar as APIs de Fila de Trabalho RT.

#define INVALID_WORK_QUEUE_ID 0xffffffff
DWORD g_WorkQueueId = INVALID_WORK_QUEUE_ID;
//#define MMCSS_AUDIO_CLASS    L"Audio"
//#define MMCSS_PROAUDIO_CLASS L"ProAudio"

STDMETHODIMP TestClass::GetParameters(DWORD* pdwFlags, DWORD* pdwQueue)
{
       HRESULT hr = S_OK;
       *pdwFlags = 0;
       *pdwQueue = g_WorkQueueId;
       return hr;
}

//-------------------------------------------------------
STDMETHODIMP TestClass::Invoke(IRtwqAsyncResult* pAsyncResult)
{
       HRESULT hr = S_OK;
       IUnknown *pState = NULL;
       WCHAR className[20];
       DWORD  bufferLength = 20;
       DWORD taskID = 0;
       LONG priority = 0;

       printf("Callback is invoked pAsyncResult(0x%0x)  Current process id :0x%0x Current thread id :0x%0x\n", (INT64)pAsyncResult, GetCurrentProcessId(), GetCurrentThreadId());

       hr = RtwqGetWorkQueueMMCSSClass(g_WorkQueueId, className, &bufferLength);
       IF_FAIL_EXIT(hr, Exit);

       if (className[0])
       {
              hr = RtwqGetWorkQueueMMCSSTaskId(g_WorkQueueId, &taskID);
              IF_FAIL_EXIT(hr, Exit);

              hr = RtwqGetWorkQueueMMCSSPriority(g_WorkQueueId, &priority);
              IF_FAIL_EXIT(hr, Exit);
              printf("MMCSS: [%ws] taskID (%d) priority(%d)\n", className, taskID, priority);
       }
       else
       {
              printf("non-MMCSS\n");
       }
       hr = pAsyncResult->GetState(&pState);
       IF_FAIL_EXIT(hr, Exit);

Exit:
       return S_OK;
}
//-------------------------------------------------------

int _tmain(int argc, _TCHAR* argv[])
{
       HRESULT hr = S_OK;
       HANDLE signalEvent;
       LONG Priority = 1;
       IRtwqAsyncResult *pAsyncResult = NULL;
       RTWQWORKITEM_KEY workItemKey = NULL;;
       IRtwqAsyncCallback *callback = NULL;
       IUnknown *appObject = NULL;
       IUnknown *appState = NULL;
       DWORD taskId = 0;
       TestClass cbClass;
       NTSTATUS status;

       hr = RtwqStartup();
       IF_FAIL_EXIT(hr, Exit);

       signalEvent = CreateEvent(NULL, true, FALSE, NULL);
       IF_TRUE_ACTION_EXIT(signalEvent == NULL, hr = E_OUTOFMEMORY, Exit);

       g_WorkQueueId = RTWQ_MULTITHREADED_WORKQUEUE;

       hr = RtwqLockSharedWorkQueue(L"Audio", 0, &taskId, &g_WorkQueueId);
       IF_FAIL_EXIT(hr, Exit);

       hr = RtwqCreateAsyncResult(NULL, reinterpret_cast<IRtwqAsyncCallback*>(&cbClass), NULL, &pAsyncResult);
       IF_FAIL_EXIT(hr, Exit);

       hr = RtwqPutWaitingWorkItem(signalEvent, Priority, pAsyncResult, &workItemKey);
       IF_FAIL_EXIT(hr, Exit);

       for (int i = 0; i < 5; i++)
       {
              SetEvent(signalEvent);
              Sleep(30);
              hr = RtwqPutWaitingWorkItem(signalEvent, Priority, pAsyncResult, &workItemKey);
              IF_FAIL_EXIT(hr, Exit);
    }

Exit:
       if (pAsyncResult)
       {
              pAsyncResult->Release();
       }

      if (INVALID_WORK_QUEUE_ID != g_WorkQueueId)
      {
        hr = RtwqUnlockWorkQueue(g_WorkQueueId);
        if (FAILED(hr))
        {
            printf("Failed with RtwqUnlockWorkQueue 0x%x\n", hr);
        }

        hr = RtwqShutdown();
        if (FAILED(hr))
        {
            printf("Failed with RtwqShutdown 0x%x\n", hr);
        }
      }

       if (FAILED(hr))
       {
          printf("Failed with error code 0x%x\n", hr);
       }
       return 0;
}

Por fim, os desenvolvedores de aplicativos que usam WASAPI precisam marcar seus fluxos com a categoria de áudio e se devem usar o modo de processamento de sinal bruto, com base na funcionalidade de cada fluxo. A Microsoft recomenda que todos os fluxos de áudio não usem o modo de processamento de sinal bruto, a menos que as implicações sejam compreendidas. O modo bruto ignora todo o processamento de sinal que foi escolhido pelo OEM, portanto:

  • O sinal de renderização para um ponto de extremidade específico pode ser abaixo do ideal.
  • O sinal de captura pode vir em um formato que o aplicativo não consegue entender.
  • A latência pode ser aprimorada.

Melhorias no driver

Para que os drivers de áudio ofereçam suporte à baixa latência, Windows 10 e posteriores fornecem os seguintes recursos:

  1. [Obrigatório] Declare o tamanho mínimo do buffer com suporte em cada modo.
  2. [Opcional, mas recomendado] Melhore a coordenação para o fluxo de dados entre o driver e o Windows.
  3. [Opcional, mas recomendado] Registre os recursos de driver (interrupções, threads) para que eles possam ser protegidos pelo Windows em cenários de baixa latência. Os drivers de função de miniporto HDAudio que são enumerados pelo driver de barramento HDAudio da caixa de entrada hdaudbus.sys não precisam registrar as interrupções do HDAudio, pois isso já é feito por hdaudbus.sys. No entanto, se o driver de miniporto criar seus próprios threads, ele precisará registrá-los.

As três seções a seguir explicarão cada novo recurso mais detalhadamente.

Declarar o tamanho mínimo do buffer

Um driver opera sob várias restrições ao mover dados de áudio entre o Windows, o driver e o hardware. Essas restrições podem ser devido ao transporte de hardware físico que move dados entre memória e hardware ou devido aos módulos de processamento de sinal dentro do hardware ou DSP associado.

A partir Windows 10, versão 1607, o driver pode expressar seus recursos de tamanho de buffer usando a propriedade do dispositivo DEVPKEY_KsAudio_PacketSize_Constraints2. Essa propriedade permite que o usuário defina o tamanho mínimo absoluto do buffer compatível com o driver e restrições de tamanho de buffer específicas para cada modo de processamento de sinal. As restrições específicas do modo precisam ser maiores do que o tamanho mínimo do buffer dos drivers, caso contrário, são ignoradas pela pilha de áudio.

Por exemplo, o snippet de código a seguir mostra como um driver pode declarar que o tamanho mínimo absoluto do buffer com suporte é de 2 ms, mas o modo padrão dá suporte a 128 quadros, o que corresponde a 3 ms se assumirmos uma taxa de amostragem de 48 kHz.

 
//
// Describe buffer size constraints for WaveRT buffers
//
static struct
{
    KSAUDIO_PACKETSIZE_CONSTRAINTS2 TransportPacketConstraints;
    KSAUDIO_PACKETSIZE_PROCESSINGMODE_CONSTRAINT AdditionalProcessingConstraints[1];
} SysvadWaveRtPacketSizeConstraintsRender =
{
    {
        2 * HNSTIME_PER_MILLISECOND,                // 2 ms minimum processing interval
        FILE_BYTE_ALIGNMENT,                        // 1 byte packet size alignment
        0,                                          // no maximum packet size constraint
        2,                                          // 2 processing constraints follow
        {
            STATIC_AUDIO_SIGNALPROCESSINGMODE_DEFAULT,          // constraint for default processing mode
            128,                                                // 128 samples per processing frame
            0,                                                  // NA hns per processing frame
        },
    },
    {
        {
            STATIC_AUDIO_SIGNALPROCESSINGMODE_MOVIE,            // constraint for movie processing mode
            1024,                                               // 1024 samples per processing frame
            0,                                                  // NA hns per processing frame
        },
    }
};

Consulte os seguintes artigos para obter informações mais detalhadas sobre essas estruturas:

Além disso, o exemplo sysvad mostra como usar essas propriedades, para que um driver declare o buffer mínimo para cada modo.

Melhorar a coordenação entre o driver e o sistema operacional

Os DDIs descritos nesta seção permitem que o driver:

  • Indique claramente qual metade (pacote) do buffer está disponível para o Windows, em vez da adivinhação do sistema operacional com base em uma posição de link codec. Isso ajuda o Windows a se recuperar de falhas de áudio mais rapidamente.
  • Opcionalmente, otimize ou simplifique suas transferências de dados dentro e fora do buffer WaveRT. A quantidade de benefícios aqui depende do design do mecanismo de DMA ou de outro mecanismo de transferência de dados entre o buffer WaveRT e o hardware (possivelmente DSP).
  • "Intermitência" capturou dados mais rápido do que em tempo real se o driver tiver acumulado internamente os dados capturados. Isso se destina principalmente a cenários de ativação de voz, mas também pode ser aplicado durante o streaming normal.
  • Forneça informações de carimbo de data/hora sobre sua posição de fluxo atual em vez de adivinhação do Windows, potencialmente permitindo informações precisas de posição.

Essa DDI é útil no caso em que um DSP é usado. No entanto, um driver de áudio HD padrão ou outros designs de buffer de DMA circular simples podem não encontrar muitos benefícios nesses novos DDIs listados aqui.

Várias das rotinas de driver retornam carimbos de data/hora do contador de desempenho do Windows que refletem a hora em que as amostras são capturadas ou apresentadas pelo dispositivo.

Em dispositivos que têm pipelines DSP complexos e processamento de sinal, calcular um carimbo de data/hora preciso pode ser desafiador e deve ser feito com consideração. Os carimbos de data/hora não devem refletir a hora em que as amostras foram transferidas para ou do Windows para o DSP.

Para calcular os valores do contador de desempenho, o driver e o DSP podem empregar alguns dos métodos a seguir.

  • No DSP, acompanhe os carimbos de data/hora de exemplo usando algum relógio de parede DSP interno.
  • Entre o driver e o DSP, calcule uma correlação entre o contador de desempenho do Windows e o relógio de parede DSP. Os procedimentos para isso podem variar de simples (mas menos precisos) a bastante complexos ou novos (mas mais precisos).
  • Considere os atrasos constantes devido a algoritmos de processamento de sinal ou transportes de pipeline ou hardware, a menos que esses atrasos sejam contabilizados de outra forma.

O exemplo sysvad mostra como usar os DDIs acima.

Registrar recursos de driver

Para ajudar a garantir a operação sem falhas, os drivers de áudio devem registrar seus recursos de streaming com o Portcls. Isso permite que o Windows gerencie recursos para evitar interferências entre streaming de áudio e outros subsistemas.

Os recursos de fluxo são todos os recursos usados pelo driver de áudio para processar fluxos de áudio ou garantir o fluxo de dados de áudio. Há suporte apenas para dois tipos de recursos de fluxo: interrupções e threads de propriedade do driver. Os drivers de áudio devem registrar um recurso depois de criar o recurso e cancelar o registro do recurso antes de excluí-lo.

Os drivers de áudio podem registrar recursos no momento da inicialização quando o driver é carregado ou em tempo de execução, por exemplo, quando há um rebalanceamento de recursos de E/S. Portcls usa um estado global para acompanhar todos os recursos de streaming de áudio.

Em alguns casos de uso, como aqueles que exigem áudio de latência muito baixa, o Windows tenta isolar os recursos registrados do driver de áudio contra interferências de outros sistemas operacionais, aplicativos e atividades de hardware. O sistema operacional e o subsistema de áudio fazem isso conforme necessário sem interagir com o driver de áudio, exceto pelo registro dos recursos pelo driver de áudio.

Esse requisito para registrar recursos de fluxo implica que todos os drivers que estão no caminho do pipeline de streaming devem registrar seus recursos direta ou indiretamente com Portcls. O driver de miniporto de áudio tem estas opções:

  • O driver de miniporto de áudio é o driver inferior de sua pilha (interfacing o h/w diretamente), nesse caso, o driver conhece seus recursos de fluxo e pode registrá-los com Portcls.
  • O driver de miniporto de áudio está transmitindo áudio com a ajuda de outros drivers (por exemplo, motoristas de barramento de áudio). Esses outros drivers também usam recursos que devem ser registrados com Portcls. Essas pilhas de driver paralelo/de barramento podem expor uma interface pública (ou privada, se um único fornecedor possuir todos os drivers) que os drivers de miniporto de áudio usam para coletar essas informações.
  • O driver de miniporto de áudio está transmitindo áudio com a ajuda de outros drivers (por exemplo, hdaudbus). Esses outros drivers também usam recursos que devem ser registrados com Portcls. Esses drivers paralelos/de barramento podem se vincular ao Portcls e registrar diretamente seus recursos. Os drivers de miniporto de áudio devem informar aos Portcls que eles dependem dos recursos desses outros PDOs (dispositivos paralelos/de barramento). A infraestrutura de áudio HD usa essa opção, ou seja, o driver de barramento de áudio HD é vinculado ao Portcls e executa automaticamente as seguintes etapas:
    • registra os recursos de seu motorista de ônibus e
    • notifica o Portcls de que os recursos dos filhos dependem dos recursos do pai. Na arquitetura de áudio HD, o driver de miniporto de áudio só precisa registrar seus próprios recursos de thread de propriedade do driver.

Observações:

  • Os drivers de função de miniporto HDAudio que são enumerados pelo driver de barramento HDAudio da caixa de entrada hdaudbus.sys não precisam registrar as interrupções do HDAudio, pois isso já é feito por hdaudbus.sys. No entanto, se o driver de miniporto criar seus próprios threads, ele precisará registrá-los.
  • Os drivers vinculados ao Portcls somente para registrar recursos de streaming devem atualizar seus INFs para incluir wdmaudio.inf e copiar portcls.sys (e arquivos dependentes). Uma nova seção de cópia INF é definida em wdmaudio.inf para copiar apenas esses arquivos.
  • Drivers de áudio executados apenas em Windows 10 e posteriores podem ser vinculados a:
  • Os drivers de áudio que devem ser executados em um sistema operacional de nível inferior podem usar a interface a seguir (o miniporto pode chamar QueryInterface para a interface IID_IPortClsStreamResourceManager e registrar seus recursos somente quando PortCls der suporte à interface).
  • Esses DDIs usam essa enumeração e estrutura:

Por fim, os drivers que vinculam PortCls com a única finalidade de registrar recursos devem adicionar as duas linhas a seguir na seção DDInstall do inf. Os drivers de miniporto de áudio não precisam disso porque já têm inclusões/necessidades em wdmaudio.inf.

[<install-section-name>]
Include=wdmaudio.inf
Needs=WDMPORTCLS.CopyFilesOnly

As linhas acima garantem que PortCls e seus arquivos dependentes estejam instalados.

Ferramentas de medição

Para medir a latência de ida e volta, o usuário pode utilizar ferramentas que reproduzam pulsos por meio dos alto-falantes e capturá-los por meio do microfone. Eles medem o atraso do seguinte caminho:

  1. O aplicativo chama a API de renderização (AudioGraph ou WASAPI) para reproduzir o pulso
  2. O áudio é reproduzido por meio dos alto-falantes
  3. O áudio é capturado do microfone
  4. O pulso é detectado pela API de captura (AudioGraph ou WASAPI) Para medir a latência de ida e volta para diferentes tamanhos de buffer, os usuários precisam instalar um driver que dê suporte a buffers pequenos. O driver HDAudio da caixa de entrada foi atualizado para dar suporte a tamanhos de buffer entre 128 amostras (2.66ms@48kHz) e 480 amostras (10ms@48kHz). As etapas a seguir mostram como instalar o driver HDAudio da caixa de entrada (que faz parte de todos os SKUs Windows 10 e posteriores):
  • Abra o Gerenciador de Dispositivos.
  • Em Controladores de vídeo e jogos de som, clique duas vezes no dispositivo que corresponde aos alto-falantes internos.
  • Na próxima janela, vá para a guia Driver .
  • Selecione Atualizar driver ->Procurar software de driver no meu computador ->Deixe-me escolher uma lista de drivers de dispositivo neste computador ->Selecione Dispositivo de Áudio de Alta Definição e selecione Avançar.
  • Se uma janela intitulada "Atualizar aviso do driver" for exibida, selecione Sim.
  • Selecione fechar.
  • Se você for solicitado a reinicializar o sistema, selecione Sim para reinicializar.
  • Após a reinicialização, o sistema usará o driver microsoft HDAudio da caixa de entrada e não o driver de codec de terceiros. Lembre-se de qual driver você estava usando antes para poder voltar para esse driver se quiser usar as configurações ideais para o codec de áudio.

Grafo ilustrando diferenças de latência de ida e volta entre WASAPI e AudioGraph para vários tamanhos de buffer.

As diferenças na latência entre WASAPI e AudioGraph são devido aos seguintes motivos:

  • O AudioGraph adiciona um buffer de latência no lado da captura, a fim de sincronizar a renderização e a captura, que não é fornecida pelo WASAPI. Essa adição simplifica o código para aplicativos escritos usando AudioGraph.
  • Há outro buffer de latência no lado de renderização do AudioGraph quando o sistema está usando buffers maiores que 6 ms.
  • O AudioGraph não tem a opção de desabilitar a captura de efeitos de áudio.

Exemplos

Perguntas frequentes

Não seria melhor, se todos os aplicativos usuem as novas APIs para baixa latência? A baixa latência não garante sempre uma melhor experiência do usuário?

Não necessariamente. A baixa latência tem suas compensações:

  • Baixa latência significa maior consumo de energia. Se o sistema usar buffers de 10 ms, isso significa que a CPU será ativada a cada 10 ms, preencherá o buffer de dados e entrará em suspensão. No entanto, se o sistema usar buffers de 1 ms, isso significa que a CPU será ativada a cada 1 ms. No segundo cenário, isso significa que a CPU será ativada com mais frequência e o consumo de energia aumentará. Isso diminuirá a duração da bateria.
  • A maioria dos aplicativos depende de efeitos de áudio para fornecer a melhor experiência do usuário. Por exemplo, os media players desejam fornecer áudio de alta fidelidade. Os aplicativos de comunicação querem o mínimo de eco e ruído. Adicionar esses tipos de efeitos de áudio a um fluxo aumenta sua latência. Esses aplicativos estão mais interessados na qualidade do áudio do que na latência de áudio.

Em resumo, cada tipo de aplicativo tem necessidades diferentes em relação à latência de áudio. Se um aplicativo não precisar de baixa latência, ele não deverá usar as novas APIs para baixa latência.

Todos os sistemas que são atualizados para Windows 10 e posterior serão atualizados automaticamente para dar suporte a buffers pequenos? Todos os sistemas darão suporte ao mesmo tamanho mínimo de buffer?

Não, para que um sistema dê suporte a buffers pequenos, ele precisa ter drivers atualizados. Cabe aos OEMs decidir quais sistemas serão atualizados para dar suporte a buffers pequenos. Além disso, sistemas mais recentes são mais propensos a dar suporte a buffers menores do que sistemas mais antigos. A latência em novos sistemas provavelmente será menor do que os sistemas mais antigos.

Se um driver der suporte a tamanhos de buffer pequenos, todos os aplicativos no Windows 10 e posterior usarão automaticamente buffers pequenos para renderizar e capturar áudio?

Não, por padrão, todos os aplicativos no Windows 10 e posterior usarão buffers de 10 ms para renderizar e capturar áudio. Se um aplicativo precisar usar buffers pequenos, ele precisará usar as novas configurações do AudioGraph ou a interface WASAPI IAudioClient3 para fazer isso. No entanto, se um aplicativo solicitar o uso de buffers pequenos, o mecanismo de áudio começará a transferir áudio usando esse tamanho de buffer específico. Nesse caso, todos os aplicativos que usam o mesmo ponto de extremidade e modo alternarão automaticamente para esse tamanho de buffer pequeno. Quando o aplicativo de baixa latência for encerrado, o mecanismo de áudio mudará para buffers de 10 ms novamente.