Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Conforme explicado anteriormente, se um aplicativo abrir um fluxo no modo exclusivo, o aplicativo terá o uso exclusivo do dispositivo de ponto de extremidade de áudio que reproduz ou registra o fluxo. Por outro lado, diversos aplicativos podem compartilhar um dispositivo de ponto de extremidade de áudio abrindo fluxos em modo compartilhado no dispositivo.
Dica
Começando com Windows 10, o IAudioClient3 permite fluxos de modo compartilhado com tamanhos de período muito baixos (tão baixos quanto o valor retornado por GetSharedModeEnginePeriod). Isso fornece latência comparável ao modo exclusivo, preservando a interoperabilidade e a combinação de som do sistema. Avalie fluxos em período de baixa no modo compartilhado antes de confirmar o modo exclusivo, a menos que você tenha um requisito de saída bit a bit exata ou taxas de amostragem personalizadas.
Aviso
Fluxos de áudio em modo exclusivo capturam o ponto de extremidade de áudio – todos os sons de sistema, notificações e áudio de outros aplicativos são silenciados. Embora o acesso de modo exclusivo esteja habilitado por padrão nas configurações de som, os usuários podem desabilitá-lo por dispositivo. Sempre forneça uma opção visível ao usuário para voltar ao modo compartilhado e libere o dispositivo imediatamente quando seu aplicativo perder o foco.
O acesso de modo exclusivo a um dispositivo de áudio pode bloquear sons cruciais do sistema, impedir a interoperabilidade com outros aplicativos e, de outra forma, prejudicar a experiência do usuário. Para atenuar esses problemas, um aplicativo com um fluxo de modo exclusivo normalmente abre mão do controle do dispositivo de áudio quando o aplicativo não é o processo em primeiro plano nem está transmitindo ativamente.
A latência do fluxo é o atraso inerente ao caminho de dados que conecta o buffer do ponto de extremidade de um aplicativo a um dispositivo do ponto de extremidade de áudio. Para um fluxo de renderização, a latência é o atraso máximo desde o momento em que um aplicativo grava um exemplo em um buffer de ponto de extremidade até o momento em que o exemplo é ouvido pelos alto-falantes. Para um fluxo de captura, a latência é o atraso máximo entre o momento em que um som entra no microfone e o momento em que um aplicativo consegue ler a amostra desse som com base no buffer do ponto de extremidade.
Aplicativos que usam fluxos em modo exclusivo normalmente fazem isso porque exigem baixas latências nos fluxos de dados entre os dispositivos de ponto de extremidade de áudio e as threads de aplicativo que acessam os buffers do ponto de extremidade. Normalmente, esses threads são executados com prioridade relativamente alta e se agendam para serem executados em intervalos periódicos próximos ou iguais ao intervalo periódico que separa as sucessivas passagens de processamento pelo hardware de áudio. Durante cada passagem, o hardware de áudio processa os novos dados nos buffers do ponto de extremidade.
Para obter as menores latências de fluxo, um aplicativo pode exigir hardware de áudio especial e um sistema de computador levemente carregado. Conduzir o hardware de áudio além de seus limites de tempo ou carregar o sistema com tarefas de alta prioridade concorrentes pode causar uma falha em um fluxo de áudio de baixa latência. Por exemplo, para um fluxo de renderização, poderá ocorrer uma falha se o aplicativo deixar de gravar em um buffer do ponto de extremidade antes do hardware de áudio ler o buffer ou se o hardware não ler o buffer antes da hora na qual o buffer está agendado para ser reproduzido. Normalmente, um aplicativo que se destina a ser executado em uma ampla variedade de hardware de áudio e em uma ampla gama de sistemas deve relaxar seus requisitos de tempo suficientes para evitar falhas em todos os ambientes de destino.
O Windows Vista tem vários recursos para dar suporte a aplicativos que exigem fluxos de áudio de baixa latência. Conforme discutido em Componentes de Áudio no Modo de Usuário, os aplicativos que executam operações com restrições de tempo podem chamar as funções do Serviço de Agendamento de Classes Multimídia (MMCSS) para aumentar a prioridade da thread sem privar aplicativos de menor prioridade de recursos da CPU. Além disso, o método IAudioClient::Initialize oferece suporte ao sinalizador AUDCLNT_STREAMFLAGS_EVENTCALLBACK, que permite que a thread de atendimento ao buffer de um aplicativo agende sua execução para quando um novo buffer for disponibilizado pelo dispositivo de áudio. Usando esses recursos, um thread de aplicativo pode reduzir a incerteza sobre quando ele será executado, diminuindo assim o risco de falhas em um fluxo de áudio de baixa latência.
Os drivers para adaptadores de áudio mais antigos tendem a usar a interface de driver de dispositivo (DDI) WaveCyclic ou WavePci, enquanto os drivers para adaptadores de áudio mais recentes têm maior probabilidade de oferecer suporte à DDI WaveRT. Para aplicativos de modo exclusivo, os drivers WaveRT podem fornecer melhor desempenho do que os drivers WaveCyclic ou WavePci, mas os drivers WaveRT exigem recursos de hardware adicionais. Esses recursos incluem a capacidade de compartilhar buffers de hardware diretamente com aplicativos. Com o compartilhamento direto, nenhuma intervenção do sistema é necessária para transferir dados entre um aplicativo de modo exclusivo e o hardware de áudio. Por outro lado, os drivers WaveCyclic e WavePci são adequados para adaptadores de áudio mais antigos e menos capazes. Esses adaptadores dependem do software do sistema para transportar blocos de dados (anexados a pacotes de solicitação de E/S do sistema ou IRPs) entre buffers de aplicativos e buffers de hardware. Além disso, os dispositivos de áudio USB dependem do software do sistema para transportar dados entre buffers de aplicativo e buffers de hardware. Para melhorar o desempenho de aplicativos de modo exclusivo que se conectam a dispositivos de áudio que dependem do sistema para transporte de dados, o WASAPI aumenta automaticamente a prioridade dos threads do sistema que transferem dados entre os aplicativos e o hardware. O WASAPI usa o MMCSS para aumentar a prioridade do thread. No Windows Vista, se um thread do sistema gerencia o transporte de dados para um fluxo de reprodução de áudio de modo exclusivo com um formato PCM e um período de dispositivo de menos de 10 milissegundos, WASAPI atribui o nome da tarefa MMCSS "Pro Audio" ao thread. Se o período de dispositivo do fluxo for maior que ou igual a 10 milissegundos, WASAPI atribuirá o nome da tarefa MMCSS "Áudio" ao thread. Para obter mais informações sobre os DDIs WaveCyclic, WavePci e WaveRT, consulte a documentação do DDK do Windows. Para obter informações sobre como selecionar um período de dispositivo apropriado, consulte IAudioClient::GetDevicePeriod.
Conforme descrito em controles de volume de sessão, WASAPI fornece as interfaces ISimpleAudioVolume, IChannelAudioVolumee IAudioStreamVolume para controlar os níveis de volume de fluxos de áudio de modo compartilhado. No entanto, os controles nessas interfaces não têm efeito em fluxos de modo exclusivo. Em vez disso, os aplicativos que gerenciam fluxos de modo exclusivo normalmente usam a interface IAudioEndpointVolume na API EndpointVolume para controlar os níveis de volume desses fluxos. Para obter informações sobre essa interface, consulte Controles de volume do ponto de extremidade.
Para cada dispositivo de reprodução e dispositivo de captura no sistema, o usuário pode controlar se o dispositivo pode ser usado no modo exclusivo. Se o usuário desabilitar o uso de modo exclusivo do dispositivo, o dispositivo poderá ser usado para reproduzir ou gravar somente fluxos de modo compartilhado.
Se o usuário habilitar o uso de modo exclusivo do dispositivo, o usuário também poderá controlar se uma solicitação de um aplicativo para usar o dispositivo no modo exclusivo irá prever o uso do dispositivo por aplicativos que atualmente podem estar reproduzindo ou gravando fluxos de modo compartilhado por meio do dispositivo. Se a preempção estiver habilitada, uma solicitação de um aplicativo para assumir o controle exclusivo do dispositivo terá êxito se o dispositivo não estiver em uso no momento ou se o dispositivo estiver sendo usado no modo compartilhado, mas a solicitação falhar se outro aplicativo já tiver controle exclusivo do dispositivo. Se a preempção estiver desabilitada, uma solicitação de um aplicativo para assumir o controle exclusivo do dispositivo terá êxito se o dispositivo não estiver em uso no momento, mas a solicitação falhará se o dispositivo já estiver sendo usado no modo compartilhado ou no modo exclusivo.
No Windows Vista, as configurações padrão para um dispositivo de ponto de extremidade de áudio são as seguintes:
- O dispositivo pode ser usado para reproduzir ou gravar fluxos de modo exclusivo.
- Uma solicitação para usar um dispositivo para reproduzir ou gravar um fluxo de modo exclusivo preempõe qualquer fluxo de modo compartilhado que esteja sendo reproduzido ou gravado no dispositivo.
Para alterar as configurações de modo exclusivo de um dispositivo de reprodução ou gravação
- Clique com o botão direito do mouse no ícone do alto-falante na área de notificação, que está localizada no lado direito da barra de tarefas, e selecione Dispositivos de Reprodução ou Dispositivos de Gravação. (Como alternativa, execute o painel de controle multimídia do Windows, Mmsys.cpl, em uma janela do Prompt de Comando. Para obter mais informações, consulte Comentários em DEVICE_STATE_XXX Constantes.)
- Depois que a janela Som for exibida, selecione de Reprodução ou de Gravação. Em seguida, selecione uma entrada na lista de nomes de dispositivo e clique em Propriedades.
- Depois que a janela Propriedades for exibida, clique em Avançado.
- Para permitir que os aplicativos usem o dispositivo no modo exclusivo, marque a caixa rotulada Permitir que os aplicativos assumam o controle exclusivo deste dispositivo. Para desabilitar o uso de modo exclusivo do dispositivo, desmarque a caixa de seleção.
- Se o uso de modo exclusivo do dispositivo estiver habilitado, você poderá especificar se uma solicitação de controle exclusivo do dispositivo terá êxito se o dispositivo estiver reproduzindo ou gravando fluxos de modo compartilhado no momento. Para dar prioridade a aplicativos de modo exclusivo em aplicativos de modo compartilhado, marque a caixa rotulada Dar prioridade a aplicativos de modo exclusivo. Para negar prioridade de aplicativos de modo exclusivo em aplicativos de modo compartilhado, desmarque a caixa de seleção.
O exemplo de código a seguir mostra como reproduzir um fluxo de áudio de baixa latência em um dispositivo de renderização de áudio configurado para uso no modo exclusivo:
//-----------------------------------------------------------
// 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;
}
No exemplo de código anterior, a função PlayExclusiveStream é executada no thread do aplicativo que atende aos buffers do ponto de extremidade enquanto um fluxo de renderização está em execução. A função usa um único parâmetro, pMySource, que é um ponteiro para um objeto que pertence a uma classe definida pelo cliente, MyAudioSource. Essa classe tem duas funções de membro, LoadData e SetFormat, que são chamadas no exemplo de código. MyAudioSource está descrito em Renderização de uma transmissão.
A função PlayExclusiveStream chama uma função auxiliar, GetStreamFormat, que negocia com o dispositivo de renderização padrão para determinar se o dispositivo dá suporte a um formato de fluxo de modo exclusivo adequado para uso pelo aplicativo. O código da função GetStreamFormat não aparece no exemplo de código; isso ocorre porque os detalhes de sua implementação dependem inteiramente dos requisitos do aplicativo. No entanto, a operação da função GetStreamFormat pode ser descrita de forma simples, ela chama o método IAudioClient::IsFormatSupported uma ou mais vezes para determinar se o dispositivo dá suporte a um formato adequado. Os requisitos do aplicativo determinam quais formatos GetStreamFormat apresenta ao método IsFormatSupported e a ordem na qual ele os apresenta. Para obter mais informações sobre IsFormatSupported, consulte Device Formats.
Após a chamada GetStreamFormat, a função PlayExclusiveStream chama o método IAudioClient::GetDevicePeriod para obter o período mínimo de dispositivo compatível com o hardware de áudio. Em seguida, a função chama o método IAudioClient::Initialize para solicitar uma duração de buffer igual ao período mínimo. Se a chamada for bem-sucedida, o método Initialize aloca dois buffers de extremidade, cada um deles com duração igual à do período mínimo. Posteriormente, quando o fluxo de áudio começar a ser executado, o aplicativo e o hardware de áudio compartilharão os dois buffers de forma "ping-pong", ou seja, enquanto o aplicativo grava em um buffer, o hardware lê do outro buffer.
Antes de iniciar o fluxo, a função PlayExclusiveStream faz o seguinte:
- Cria e registra o identificador de evento por meio do qual ele receberá notificações quando os buffers estiverem prontos para serem preenchidos.
- Preenche o primeiro buffer com dados da fonte de áudio para reduzir o atraso de quando o fluxo começa a ser executado até quando o som inicial é ouvido.
- Chama a função AvSetMmThreadCharacteristics para solicitar que o MMCSS aumente a prioridade do thread no qual o PlayExclusiveStream é executado. (Quando o fluxo para de ser executado, a chamada de função AvRevertMmThreadCharacteristics restaura a prioridade de thread original.)
Para obter mais informações sobre AvSetMmThreadCharacteristics e AvRevertMmThreadCharacteristics, consulte a documentação do SDK do Windows.
Enquanto o fluxo estiver em execução, cada iteração do while-loop no exemplo de código anterior preenche um buffer do ponto de extremidade. Entre as iterações, a chamada à função WaitForSingleObject aguarda o manipulador de eventos ser sinalizado. Quando o manipulador é sinalizado, o corpo do loop faz o seguinte:
- Chama o método IAudioRenderClient::GetBuffer para obter o próximo buffer.
- Preenche o buffer.
- Chama o método IAudioRenderClient::ReleaseBuffer para liberar o buffer.
Para obter mais informações sobre WaitForSingleObject, consulte a documentação do SDK do Windows.
Se o adaptador de áudio for controlado por um driver WaveRT, a sinalização do manipulador de eventos estará vinculada às notificações de transferência por DMA do hardware de áudio. Para um dispositivo de áudio USB ou para um dispositivo de áudio controlado por um driver WaveCyclic ou WavePci, a sinalização do manipulador de eventos está vinculada a conclusões dos IRPs que transferem dados do buffer de aplicativo para o buffer de hardware.
O exemplo de código anterior leva o hardware de áudio e o sistema computacional aos seus limites de desempenho. Primeiro, para reduzir a latência do fluxo, o aplicativo agenda o thread de entrega do buffer para usar o período de dispositivo mínimo ao qual o hardware de áudio dará suporte. Em segundo lugar, para garantir que o thread seja executado de forma confiável em cada período de dispositivo, a chamada de função AvSetMmThreadCharacteristics define o parâmetro TaskName como "Pro Audio", que é, no Windows Vista, o nome da tarefa padrão com a prioridade mais alta. Considere se os requisitos de tempo do aplicativo podem ser relaxados sem comprometer sua utilidade. Por exemplo, o aplicativo pode agendar o thread de entrega do buffer para usar um período maior que o mínimo. Um período mais longo pode permitir com segurança o uso de uma prioridade de thread mais baixa.
Tópicos relacionados