Partilhar via


Áudio de baixa latência

Este artigo discute as alterações de latência de áudio no Windows 10. Ele abrange opções de API para desenvolvedores de aplicativos e alterações nos drivers que podem ser feitas para suportar áudio de baixa latência. A latência de áudio é o atraso entre o momento em que o som é criado e quando é ouvido. Ter baixa latência de áudio é importante para vários cenários-chave, como:

  • Áudio profissional
  • Criação musical
  • Comunicações
  • Realidade virtual
  • Jogos

Os objetivos deste documento são os seguintes:

  1. Descreva as fontes de latência de áudio no Windows.
  2. Explique as alterações que reduzem a latência de áudio na estrutura de som do Windows 10.
  3. Fornecer uma referência sobre como os 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 abrange:

  1. O AudioGraph API para cenários interativos e de criação de mídia.
  2. Alterações no WASAPI para suportar baixa latência.
  3. Melhorias nos DDIs do controlador.

Terminologia

Vigência Descrição
Latência de renderização Atraso entre o momento 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.
Latência de captura Atraso entre o momento 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 momento em que um som é captado a partir do microfone, processado pela aplicação e apresentado pela aplicação para renderização aos altifalantes. É aproximadamente igual a latência de renderização + latência de captura.
Latência de toque para aplicativo Atraso entre o momento em que um usuário toca na tela até o momento em que o sinal é enviado para o aplicativo.
Latência de toque ao som Atraso entre o momento em que um utilizador toca na tela, o evento é enviado para a aplicação e um som é ouvido pelos altifalantes. É igual a latência de renderização + latência de toque para aplicativo.

Pilha de áudio do Windows

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

Diagrama que mostra a pilha de áudio de baixa latência com aplicações, motor 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 objetos de processamento de áudio (APOs). Para mais informações sobre os objetos de processamento de áudio do Windows (APOs), consulte .

  3. A latência dos APOs varia com base no processamento do sinal dentro dos APOs.

  4. Antes do 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. No 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 do Windows 10, o buffer era sempre definido como ~10 ms.

  8. A partir do Windows 10, o tamanho do buffer é definido pelo driver de áudio (mais detalhes sobre o buffer são descritos mais adiante 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 do Windows 10, esse buffer sempre era definido como 10 ms.

  5. A partir do 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 objetos de processamento de áudio (APOs).

  7. A latência dos APOs varia com base no processamento do sinal dentro dos APOs.

  8. Antes do 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. No Windows 10 e posterior, a latência foi reduzida para ~0ms para todos os aplicativos.

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

Outra alternativa popular para aplicações que precisam de baixa latência é usar o modelo ASIO (Audio Stream Input/Output), 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 tem que ser escrito de tal forma que ele fala 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 motor de áudio foi modificado, a fim de reduzir a latência, mantendo a flexibilidade.

Melhorias na pilha de áudio

O Windows 10 e versões posteriores 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 quaisquer alterações de código ou atualizações de driver, em comparação com o Windows 8.1.
    1. Os aplicativos que usam dados de ponto flutuante terão latência 16 ms menor.
    2. Os aplicativos que usam dados inteiros terão latência 4,5 ms menor.
  2. Os sistemas com drivers atualizados fornecerão latência de ida e volta ainda menor:
    1. Os drivers podem usar as DDIs de baixa latência para relatar os tamanhos suportados 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 acontecia nas versões anteriores do Windows. Em vez disso, o driver pode especificar se ele pode usar buffers pequenos, por exemplo, 5 ms, 3 ms, 1 ms, etc.
    2. Os aplicativos que exigem baixa latência podem usar as APIs de áudio de baixa latência (AudioGraph ou WASAPI), para consultar os tamanhos de buffer suportados pelo 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, onde gerencia seus recursos de forma a evitar interferências 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 seu modo de execução normal. O subsistema de áudio consiste nos seguintes recursos:
    1. O fio 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 as DDIs de baixa latência descritas na seção sobre registro de recursos do driver).
    3. Alguns ou todos os fluxos de áudio das aplicações que solicitam buffers pequenos e de todas as aplicações que partilham o mesmo grafo de dispositivo de áudio (por exemplo, o mesmo modo de processamento de sinal) com qualquer aplicação que solicitou buffers pequenos:
  4. Retornos de chamada do AudioGraph no caminho de streaming.
  5. Se a aplicação usa WASAPI, então apenas os itens de trabalho que foram enviados à API de Fila de Trabalho Real-Time ou à MFCreateMFByteStreamOnStreamEx e foram etiquetados como "Áudio" ou "ProÁudio".

Melhorias na API

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

Para determinar qual das duas APIs usar:

  • Privilegie o AudioGraph, sempre que possível para o desenvolvimento de novas aplicações.
  • Utilize WASAPI apenas 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 medição deste artigo, mostra medições 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 atinja a latência mínima, ele precisa ter drivers atualizados que suportem pequenos tamanhos de buffer.

AudioGraph

O AudioGraph é uma API da Plataforma Universal do Windows no Windows 10 e posterior que visa realizar cenários interativos e de criação de música 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 . Esta propriedade pode ser qualquer um dos valores mostrados na tabela abaixo:

Valor Descrição
Padrão do sistema Define o buffer para o tamanho padrão do buffer (~10 ms)
Latência Mais Baixa Define o buffer para o valor mínimo que é suportado pelo driver
MaisPertoDoDesejado Define o tamanho do buffer como igual ao valor definido pela propriedade DesiredSamplesPerQuantum ou a um valor tão próximo de DesiredSamplesPerQuantum quanto é suportado pelo driver.

O exemplo AudioCreation mostra como usar o AudioGraph para baixa latência. O trecho 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);

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

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

  • Permita que um aplicativo descubra a gama de tamanhos de buffer (ou seja, valores de periodicidade) que são suportados pelo driver de áudio de um determinado dispositivo de áudio. Isso torna possível para um aplicativo escolher entre o tamanho padrão do buffer (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 de buffer padrão.
  • Permita 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 nova amostragem pelo mecanismo de áudio

Os recursos acima estarão disponíveis em todos os dispositivos Windows. No entanto, certos 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 interface, chamada IAudioClient3, que deriva de IAudioClient2.

IAudioClient3 define os seguintes 3 métodos:

Método Descrição
GetCurrentSharedModeEnginePeriod Devolve o formato atual e a periodicidade do motor de áudio
GetSharedModeEnginePeriod Devolve o intervalo de periodicidades suportado pelo motor para o formato de fluxo especificado
InicializarFluxoDeÁudioPartilhado Inicializa um fluxo compartilhado com a periodicidade especificada

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

O trecho 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 suportada pelo 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 o Real-Time Work Queue API ou o MFCreateMFByteStreamOnStreamEx para criar itens de trabalho e marcá-los como Áudio ou Pro Áudio, em vez de os seus próprios threads. Isso permitirá que o Windows os gerencie de forma a evitar interferências em subsistemas que não sejam de áudio. Em contraste, todos os threads do AudioGraph são gerenciados automaticamente corretamente pelo Windows. O trecho de código a seguir do exemplo WASAPIAudio mostra como usar as APIs MF Work Queue.

// 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 trecho de código a seguir mostra como usar as APIs da 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;
}

Finalmente, 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, assim:

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

Melhorias nos controladores

Para que os drivers de áudio suportem baixa latência, o Windows 10 e posterior fornecem os seguintes recursos:

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

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

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 a memória e o hardware, ou devido aos módulos de processamento de sinal dentro do hardware ou DSP associado.

A partir do Windows 10, versão 1607, o driver pode expressar os 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 que é suportado pelo driver e restrições específicas de tamanho do buffer 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 pelo sistema de áudio.

Por exemplo, o trecho de código a seguir mostra como um driver pode declarar que o tamanho mínimo absoluto do buffer suportado é de 2 ms, mas o modo padrão suporta 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 de 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 condutor:

  • Indique claramente qual metade (pacote) do buffer está disponível para o Windows, em vez de o sistema operativo adivinhar com base em posição de link de 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 magnitude do benefício aqui depende do design da engine DMA ou de outro mecanismo de transferência de dados entre o buffer WaveRT e possivelmente o hardware DSP.
  • "‘Burst’ captura dados mais rapidamente do que em tempo real se o driver tiver acumulado internamente os dados capturados." Isso é destinado principalmente para 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ções do Windows, potencialmente permitindo informações de posição precisas.

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

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

Em dispositivos que têm pipelines DSP complexos e processamento de sinal, calcular uma marca temporal precisa pode ser um desafio e deve ser feito com cuidado. Os carimbos de data/hora não devem refletir a hora em que as amostras foram transferidas de ou para o Windows para o DSP.

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

  • Dentro do DSP, rastreie os carimbos temporais de amostra usando um relógio interno do DSP.
  • 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 o efeito podem variar de simples (mas menos precisos) a bastante complexos ou novos (mas mais precisos).
  • Leve em conta quaisquer atrasos constantes devido a algoritmos de processamento de sinal, pipeline ou transporte de hardware, a menos que esses atrasos sejam contabilizados de outra forma.

O exemplo de sysvad mostra como usar os DDIs acima.

Registrar recursos do controlador

Para ajudar a garantir uma 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 o streaming de áudio e outros subsistemas.

Recursos de fluxo são quaisquer recursos usados pelo driver de áudio para processar fluxos de áudio ou garantir o fluxo de dados de áudio. Apenas dois tipos de recursos de fluxo são suportados: interrupções e threads controladas pelo 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 reequilíbrio de recursos de E/S. O 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 da interferência 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 para o registro dos recursos pelo driver de áudio.

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

  • O driver de miniporta de áudio é o driver inferior da sua pilha (interfaciano diretamente com o hardware). Neste caso, o driver conhece os seus recursos de fluxos de dados e pode registrá-los no Portcls.
  • O controlador de miniporta de áudio está a transmitir áudio com a ajuda de outros controladores (por exemplo, controladores de barramento de áudio). Esses outros drivers também usam recursos que devem ser registrados no Portcls. Essas pilhas de drivers paralelos/de barramento podem expor uma interface pública (ou privada, se um único fornecedor possuir todos os drivers) que os drivers de miniporta de áudio usam para coletar essas informações.
  • O driver de miniporta de áudio está a transmitir áudio com a ajuda de outros drivers (exemplo: hdaudbus). Esses outros drivers também usam recursos que devem ser registrados no Portcls. Esses motoristas paralelos/de ônibus podem se conectar com Portcls e registrar diretamente seus recursos. Os drivers de miniporto de áudio devem informar ao Portcls que dependem dos recursos desses outros dispositivos paralelos/bus (PDOs). A infraestrutura de áudio HD utiliza esta opção, isto é, o driver de barramento de áudio HD conecta-se ao Portcls e executa automaticamente os seguintes passos:
    • regista os recursos do motorista do autocarro, e
    • notifica a Portcls de que os recursos das crianças dependem dos recursos dos pais. Na arquitetura de áudio HD, o driver de miniporta de áudio só precisa registrar os seus próprios recursos de thread de propriedade do driver.

Observações:

  • Os drivers de função miniporta HDAudio enumerados pelo driver de barramento HDAudio nativo hdaudbus.sys não precisam registrar as interrupções HDAudio, pois isso já é feito pelo hdaudbus.sys. No entanto, se o driver de miniporta criar seus próprios threads, ele precisará registrá-los.
  • Os drivers que se vinculam ao Portcls apenas 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.
  • Os drivers de áudio que só são executados no Windows 10 e posteriores podem criar links para:
  • Os drivers de áudio que devem ser executados em um sistema operacional de nível inferior podem usar a seguinte interface (a miniporta pode chamar QueryInterface para a interface IID_IPortClsStreamResourceManager e registrar seus recursos somente quando PortCls suporta a interface).
  • Esses DDIs, usam esta enumeração e estrutura:

Finalmente, os drivers que vinculam PortCls com o único propósito de registrar recursos devem adicionar as duas linhas a seguir na seção DDInstall de seu inf. Os drivers de miniportas de áudio não precisam disso porque já têm inclusões/necessidades no wdmaudio.inf.

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

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

Ferramentas de medição

A fim de medir a latência de ida e volta, o usuário pode utilizar ferramentas que reproduzem pulsos através dos alto-falantes e capturá-los através 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 através dos altifalantes
  3. O áudio é captado a partir do microfone
  4. O pulso é detetado 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 suporte buffers pequenos. O driver HDAudio da caixa de entrada foi atualizado para suportar 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 do Windows 10 e posteriores):
  • Inicie o Gestor de dispositivos.
  • Em Controladores de som, vídeo e jogos, clique duas vezes no dispositivo que corresponde aos seus alto-falantes internos.
  • Na janela seguinte, vá para o separador Driver.
  • Selecione Atualizar driver ->Procurar software de driver no meu computador ->Deixe-me escolher a partir de uma lista de drivers de dispositivo neste computador ->Selecionar Dispositivo de Áudio de Alta Definição e selecione Avançar.
  • Se aparecer uma janela intitulada "Aviso de atualização de driver", selecione Sim.
  • Selecione fechar.
  • Se lhe for pedido para reiniciar o sistema, selecione Sim para reiniciar.
  • Após a reinicialização, o sistema estará usando 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 que você possa voltar para esse driver se você quiser usar as configurações ideais para o seu codec de áudio.

Gráfico ilustrando as 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 devidas às seguintes razões:

  • O AudioGraph adiciona um buffer de latência no lado da captura, a fim de sincronizar a renderização e a captura, o que não é fornecido pelo WASAPI. Esta 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 desativar os efeitos de captura de áudio.

Amostras

Perguntas Frequentes

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

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

  • Baixa latência significa maior consumo de energia. Se o sistema usa buffers de 10 ms, isso significa que a CPU acordará a cada 10 ms, preencherá o buffer de dados e entrará em suspensão. No entanto, se o sistema usa buffers de 1 ms, isso significa que a CPU será ativada a cada 1 ms. No segundo cenário, isso significa que a CPU acordará com mais frequência e o consumo de energia aumentará. Isso diminuirá a vida útil da bateria.
  • A maioria dos aplicativos depende de efeitos de áudio para fornecer a melhor experiência ao usuário. Por exemplo, os players de mídia querem fornecer áudio de alta fidelidade. As aplicações 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. Estas aplicações estão mais interessadas na qualidade de á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 precisa de baixa latência, ele não deve usar as novas APIs para baixa latência.

Todos os sistemas que atualizam para o Windows 10 e posteriores serão atualizados automaticamente para suportar pequenos buffers? Todos os sistemas suportarão o mesmo tamanho mínimo de buffer?

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

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

Não, por padrão, todos os aplicativos no Windows 10 e posteriores usarão buffers de 10 ms para renderizar e capturar áudio. Se um aplicativo precisa usar buffers pequenos, então ele precisa 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, todas as aplicações que usam o mesmo endpoint e modo mudarão automaticamente para esse pequeno tamanho de buffer. Quando a aplicação de baixa latência terminar, o motor de áudio alternará para buffers de 10 ms novamente.