Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Como explicado anteriormente, se um aplicativo abre um fluxo no modo exclusivo, o aplicativo tem uso exclusivo do dispositivo de ponto final de áudio que reproduz ou grava o fluxo. Em contraste, vários aplicativos podem compartilhar um dispositivo de ponto final de áudio abrindo fluxos de modo compartilhado no dispositivo.
Tip
A partir do Windows 10, o IAudioClient3 permite fluxos em modo partilhado com períodos muito baixos (tão baixos quanto o valor devolvido pelo GetSharedModeEnginePeriod). Isto proporciona uma latência comparável ao modo exclusivo, preservando a mistura de som do sistema e a interoperabilidade. Avalie os fluxos de áudio de período curto em modo partilhado antes de optar pelo modo exclusivo, a menos que tenha uma necessidade estrita de saída bit-exata ou de taxas de amostragem personalizadas.
Warning
Os fluxos em modo exclusivo assumem o controlo do dispositivo de saída de áudio — todos os sons do sistema, notificações e o áudio de outras aplicações são silenciados. Embora o acesso em modo exclusivo esteja ativado por defeito nas definições de Som, os utilizadores podem desativá-lo por dispositivo. Forneça sempre uma opção visível ao utilizador para voltar ao modo partilhado e liberte o dispositivo rapidamente quando a sua aplicação perder o foco.
O acesso em modo exclusivo a um dispositivo de áudio pode bloquear sons cruciais do sistema, impedir a interoperabilidade com outras aplicações e degradar a experiência do utilizador. Para atenuar estes problemas, uma aplicação com um fluxo em modo exclusivo cede normalmente o controlo do dispositivo de áudio quando a aplicação não é o processo em primeiro plano ou não está a transmitir ativamente.
A latência de fluxo é o atraso inerente ao caminho de dados que conecta o buffer de ponto de extremidade de um aplicativo a um dispositivo de 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 uma amostra em um buffer de ponto de extremidade até o momento em que a amostra é ouvida através dos alto-falantes. Para um fluxo de captura, a latência é o atraso máximo desde o momento em que um som entra no microfone até o momento em que um aplicativo pode ler a amostra desse som a partir do buffer de ponto final.
Os aplicativos que usam fluxos de modo exclusivo geralmente o fazem porque exigem baixas latências nos caminhos de dados entre os dispositivos de ponto de extremidade de áudio e os threads de aplicativos que acessam os buffers de ponto de extremidade. Normalmente, esses threads são executados com prioridade relativamente alta e se programam para serem executados em intervalos periódicos próximos ou iguais ao intervalo periódico que separa sucessivas passagens de processamento pelo hardware de áudio. Durante cada passagem, o hardware de áudio processa os novos dados nos buffers de ponto final.
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 concorrentes de alta prioridade pode causar uma falha em um fluxo de áudio de baixa latência. Por exemplo, num fluxo de renderização, pode ocorrer uma interrupção se a aplicação não conseguir escrever na memória intermédia do ponto terminal antes de o hardware de áudio ler essa memória, ou se o hardware não conseguir ler a memória antes do momento em que está programada para ser reproduzida. 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 o suficiente para evitar falhas em todos os ambientes de destino.
O Windows Vista tem vários recursos para oferecer suporte a aplicativos que exigem fluxos de áudio de baixa latência. Conforme discutido em User-Mode Audio Components, os aplicativos que executam operações críticas de tempo podem chamar as funções MMCSS (Multimedia Class Scheduler Service) para aumentar a prioridade do thread sem negar recursos da CPU para aplicativos de prioridade mais baixa. Além disso, o método IAudioClient::Initialize suporta o sinalizador AUDCLNT_STREAMFLAGS_EVENTCALLBACK, que permite que a thread de serviço do buffer de uma aplicação agende a sua execução para quando um novo buffer se torna disponível no dispositivo de áudio. Ao usar esses recursos, um thread de aplicativo pode reduzir a incerteza sobre quando 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 provavelmente usarão a interface de driver de dispositivo (DDI) WaveCyclic ou WavePci, enquanto os drivers para adaptadores de áudio mais recentes são mais propensos a suportar o DDI WaveRT. Para aplicações 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. Em contraste, 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 (conectados 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 aplicativos 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. WASAPI usa MMCSS para aumentar a prioridade de 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 inferior a 10 milissegundos, o WASAPI atribui o nome da tarefa MMCSS "Pro Audio" ao thread. Se o período do dispositivo do fluxo for maior ou igual a 10 milissegundos, o WASAPI atribui o nome da tarefa MMCSS "Audio" ao thread. Para obter mais informações sobre as DDIs WaveCyclic, WavePci e WaveRT, consulte a documentação do Windows DDK. Para obter informações sobre como selecionar um período de dispositivo apropriado, consulte IAudioClient::GetDevicePeriod.
Conforme descrito em Controlos de Volume da Sessão, WASAPI fornece as interfaces ISimpleAudioVolume, IChannelAudioVolume e IAudioStreamVolume para controlar os níveis de volume de fluxos de áudio em modo partilhado. No entanto, os controles nessas interfaces não têm efeito sobre fluxos de modo exclusivo. Em vez disso, os aplicativos que gerenciam fluxos de modo exclusivo normalmente usam a interface IAudioEndpointVolume no da API EndpointVolume para controlar os níveis de volume desses fluxos. Para obter informações sobre essa interface, consulte Endpoint Volume Controls.
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 desativar o uso exclusivo do dispositivo, o dispositivo pode ser usado para reproduzir ou gravar apenas fluxos de modo compartilhado.
Se o usuário permitir o uso de modo exclusivo do dispositivo, o usuário também pode controlar se uma solicitação de um aplicativo para usar o dispositivo no modo exclusivo irá antecipar o uso do dispositivo por aplicativos que podem estar atualmente reproduzindo ou gravando fluxos de modo compartilhado através do dispositivo. Se a preempção estiver ativada, uma solicitação de um aplicativo para assumir o controle exclusivo do dispositivo será bem-sucedida 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 desativada, uma solicitação de um aplicativo para assumir o controle exclusivo do dispositivo será bem-sucedida 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 streams de modo exclusivo.
- Uma solicitação para usar um dispositivo para reproduzir ou gravar um fluxo de modo exclusivo antecipa qualquer fluxo de modo compartilhado que esteja sendo reproduzido ou gravado atualmente através do 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 rato no ícone do altifalante na área de notificação, 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 controlo multimédia do Windows, Mmsys.cpl, a partir de uma janela da Linha de Comandos. Para obter mais informações, consulte Comentários em DEVICE_STATE_XXX constantes.)
- Depois de surgir a janela Som, selecione Reprodução ou Gravação. Em seguida, selecione uma entrada na lista de nomes de dispositivos 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 desativar o uso exclusivo do dispositivo, desmarque a caixa de seleção.
- Se a utilização em modo exclusivo do dispositivo estiver ativada, pode especificar se um pedido de controlo exclusivo do dispositivo terá êxito se o dispositivo estiver atualmente a reproduzir ou a gravar fluxos em modo partilhado. Para dar prioridade às aplicações em modo exclusivo em relação às aplicações em modo partilhado, selecione a caixa com a etiqueta Dar prioridade às aplicações em modo exclusivo. Para negar prioridade aos aplicativos de modo exclusivo sobre os 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 os buffers de ponto de extremidade enquanto um fluxo de renderização está sendo reproduzido. 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 é descrito em Composição de um fluxo.
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 suporta um formato de fluxo de modo exclusivo adequado para uso pelo aplicativo. O código para a função GetStreamFormat não aparece no exemplo de código; Tal deve-se ao facto de os pormenores da sua aplicação dependerem inteiramente dos requisitos do pedido. No entanto, a operação da função GetStreamFormat pode ser descrita de forma simples — ela chama o IAudioClient::IsFormatSupported método uma ou mais vezes para determinar se o dispositivo suporta um formato adequado. Os requisitos do aplicativo ditam quais formatos GetStreamFormat apresenta ao IsFormatSupported método e a ordem em que ele os apresenta. Para obter mais informações sobre IsFormatSupported , consulte Device Formats.
Após a chamada a GetStreamFormat, a função PlayExclusiveStream chama o método IAudioClient::GetDevicePeriod para obter o período mínimo do dispositivo suportado pelo dispositivo 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 com uma duração igual ao período mínimo. Mais tarde, quando o fluxo de áudio começar a ser executado, o aplicativo e o hardware de áudio compartilharão os dois buffers de forma "pingue-pongue", 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 através do qual ele receberá notificações quando os buffers estiverem prontos para preenchimento.
- 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 serviço MMCSS aumente a prioridade da thread em que 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 está em execução, cada iteração do enquanto-loop no exemplo de código anterior preenche um buffer de ponto final. Entre iterações, a chamada de função WaitForSingleObject aguarda que o identificador de evento seja sinalizado. Quando a alça é sinalizada, o corpo do loop faz o seguinte:
- Chama o IAudioRenderClient::GetBuffer método para obter o próximo buffer.
- Preenche o buffer.
- Chama o IAudioRenderClient::ReleaseBuffer método 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 evento está associada à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 controlador WaveCyclic ou WavePci, a sinalização do manipulador de evento está associada à conclusão dos IRPs que transferem dados da memória intermédia da aplicação para a memória intermédia de hardware.
O exemplo de código anterior empurra o hardware de áudio e o sistema de computador para seus limites de desempenho. Primeiro, para reduzir a latência do fluxo, a aplicação configura a thread de serviço do buffer para utilizar o período mínimo do dispositivo suportado pelo hardware de áudio. Em segundo lugar, para garantir que o thread seja executado de forma confiável dentro de cada período do 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 seu aplicativo podem ser relaxados sem comprometer sua utilidade. Por exemplo, o aplicativo pode agendar seu thread de manutenção de buffer para usar um período maior do 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