Compartilhar via


Eventos de áudio para aplicativos de áudio legados

APIs de áudio herdadas, como DirectSound, DirectShow e as funções waveOutXxx , permitem que os aplicativos obtenham e definam os níveis de volume dos fluxos de áudio. Os aplicativos podem usar os recursos de controle de volume nessas APIs para exibir controles deslizantes de volume em suas janelas de aplicativo.

No Windows Vista, o programa de controle de volume do sistema, Sndvol, permite que os usuários controlem os níveis de volume de áudio para aplicativos individuais. Os controles deslizantes de volume exibidos pelos aplicativos devem ser vinculados aos controles deslizantes de volume correspondentes no Sndvol. Se um usuário ajustar o volume do aplicativo por meio de um controle deslizante de volume em uma janela do aplicativo, o controle deslizante de volume correspondente no Sndvol será imediatamente movido para indicar o novo nível de volume. Por outro lado, se o usuário ajustar o volume do aplicativo por meio do Sndvol, os controles deslizantes de volume na janela do aplicativo deverão ser movidos para indicar o novo nível de volume.

No Windows Vista, o Sndvol reflete imediatamente as alterações de volume que um aplicativo faz por meio de chamadas para o método IDirectSoundBuffer::SetVolume ou a função waveOutSetVolume . No entanto, uma API de áudio herdada, como DirectSound ou as funções waveOutXxx , não fornece meios de notificar um aplicativo quando o usuário altera o volume do aplicativo por meio do Sndvol. Se um aplicativo exibir um controle deslizante de volume, mas não receber notificações de alterações de volume, o controle deslizante não refletirá as alterações feitas pelo usuário no Sndvol. Para implementar o comportamento apropriado, o designer de aplicativo deve de alguma forma compensar a falta de notificações pela API de áudio herdada.

Uma solução pode ser que o aplicativo defina um temporizador para lembrá-lo periodicamente de verificar o nível de volume para ver se ele foi alterado.

Uma solução mais elegante é que o aplicativo use os recursos de notificação de eventos das APIs de áudio principais. Em particular, o aplicativo pode registrar uma interface IAudioSessionEvents para receber retornos de chamada quando ocorrem alterações de volume ou outros tipos de eventos de áudio. Quando o volume muda, a rotina de retorno de chamada de alteração de volume pode atualizar imediatamente o controle deslizante de volume do aplicativo para refletir a alteração.

O exemplo de código a seguir mostra como um aplicativo pode se registrar para receber notificações de alterações de volume e outros eventos de áudio:

//-----------------------------------------------------------
// Register the application to receive notifications when the
// volume level changes on the default process-specific audio
// session (with session GUID value GUID_NULL) on the audio
// endpoint device with the specified data-flow direction
// (eRender or eCapture) and device role.
//-----------------------------------------------------------
#define EXIT_ON_ERROR(hr)  \
              if (FAILED(hr)) { goto Exit; }
#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

class AudioVolumeEvents
{
    HRESULT _hrStatus;
    IAudioSessionManager *_pManager;
    IAudioSessionControl *_pControl;
    IAudioSessionEvents *_pAudioEvents;
public:
    AudioVolumeEvents(EDataFlow, ERole, IAudioSessionEvents*);
    ~AudioVolumeEvents();
    HRESULT GetStatus() { return _hrStatus; };
};

// Constructor
AudioVolumeEvents::AudioVolumeEvents(EDataFlow flow, ERole role,
                                     IAudioSessionEvents *pAudioEvents)
{
    IMMDeviceEnumerator *pEnumerator = NULL;
    IMMDevice *pDevice = NULL;

    _hrStatus = S_OK;
    _pManager = NULL;
    _pControl = NULL;
    _pAudioEvents = pAudioEvents;

    if (_pAudioEvents == NULL)
    {
        _hrStatus = E_POINTER;
        return;
    }

    _pAudioEvents->AddRef();

    // Get the enumerator for the audio endpoint devices
    // on this system.
    _hrStatus = CoCreateInstance(__uuidof(MMDeviceEnumerator),
                                 NULL, CLSCTX_INPROC_SERVER,
                                 __uuidof(IMMDeviceEnumerator),
                                 (void**)&pEnumerator);
    EXIT_ON_ERROR(_hrStatus)

    // Get the audio endpoint device with the specified data-flow
    // direction (eRender or eCapture) and device role.
    _hrStatus = pEnumerator->GetDefaultAudioEndpoint(flow, role,
                                                     &pDevice);
    EXIT_ON_ERROR(_hrStatus)

    // Get the session manager for the endpoint device.
    _hrStatus = pDevice->Activate(__uuidof(IAudioSessionManager),
                                  CLSCTX_INPROC_SERVER, NULL,
                                  (void**)&_pManager);
    EXIT_ON_ERROR(_hrStatus)

    // Get the control interface for the process-specific audio
    // session with session GUID = GUID_NULL. This is the session
    // that an audio stream for a DirectSound, DirectShow, waveOut,
    // or PlaySound application stream belongs to by default.
    _hrStatus = _pManager->GetAudioSessionControl(NULL, 0, &_pControl);
    EXIT_ON_ERROR(_hrStatus)

    _hrStatus = _pControl->RegisterAudioSessionNotification(_pAudioEvents);
    EXIT_ON_ERROR(_hrStatus)

Exit:
    SAFE_RELEASE(pEnumerator)
    SAFE_RELEASE(pDevice)
}

// Destructor
AudioVolumeEvents::~AudioVolumeEvents()
{
    if (_pControl != NULL)
    {
        _pControl->UnregisterAudioSessionNotification(_pAudioEvents);
    }
    SAFE_RELEASE(_pManager)
    SAFE_RELEASE(_pControl)
    SAFE_RELEASE(_pAudioEvents)
};

O exemplo de código anterior implementa uma classe chamada AudioVolumeEvents. Durante a inicialização do programa, o aplicativo de áudio habilita notificações de eventos de áudio criando um objeto AudioVolumeEvents. O construtor para esta classe usa três parâmetros de entrada:

O construtor fornece os valores de fluxo e função como parâmetros de entrada para o método IMMDeviceEnumerator::GetDefaultAudioEndpoint. O método cria um objeto IMMDevice que encapsula o dispositivo de ponto de extremidade de áudio com a direção de fluxo de dados especificada e a função de dispositivo.

O aplicativo implementa o objeto apontado por pAudioEvents. (A implementação não é mostrada no exemplo de código anterior. Para obter um exemplo de código que implementa uma interface IAudioSessionEvents , consulte Eventos de sessão de áudio.) Cada método nesta interface recebe notificações de um tipo específico de evento de áudio. Se o aplicativo não estiver interessado em um tipo de evento específico, o método para esse tipo de evento não deverá fazer nada além de retornar S_OK.

O método IAudioSessionEvents::OnSimpleVolumeChanged recebe notificações de alterações de volume. Normalmente, esse método atualiza o controle deslizante de volume do aplicativo.

No exemplo de código anterior, o construtor para o AudioVolumeEvents classe registra notificações na sessão de áudio específica do processo que é identificada pelo valor GUID da sessão GUID_NULL. Por padrão, APIs de áudio herdadas, como DirectSound, DirectShow e as funções waveOutXxx, atribuem seus fluxos a esta sessão. No entanto, um aplicativo DirectSound ou DirectShow pode, como opção, substituir o comportamento padrão e atribuir seus fluxos a uma sessão entre processos ou a uma sessão identificada por um valor GUID diferente de GUID_NULL. (Nenhum mecanismo é fornecido atualmente para que um aplicativo waveOutXxx substitua o comportamento padrão de maneira semelhante.) Para obter um exemplo de código de um aplicativo DirectShow com esse comportamento, consulte Funções de dispositivo para aplicativos DirectShow. Para acomodar esse aplicativo, você pode modificar o construtor no exemplo de código anterior para aceitar dois parâmetros de entrada adicionais — um GUID de sessão e um sinalizador para indicar se a sessão a ser monitorada é uma sessão entre processos ou específica do processo. Passe esses parâmetros para a chamada para o método IAudioSessionManager::GetAudioSessionControl no construtor.

Depois que o construtor chama o método IAudioSessionControl::RegisterAudioSessionNotification para registrar notificações, o aplicativo continua a receber notificações somente enquanto a interface IAudioSessionControl ou IAudioSessionManager existir. O objeto AudioVolumeEvents no exemplo de código anterior mantém referências a essas interfaces até que seu destruidor seja chamado. Esse comportamento garante que o aplicativo continuará a receber notificações durante o tempo de vida do objeto AudioVolumeEvents.

Em vez de selecionar implicitamente um dispositivo de áudio com base em sua função de dispositivo, um DirectSound ou aplicativo multimídia herdado do Windows pode permitir que o usuário selecione explicitamente um dispositivo de uma lista de dispositivos disponíveis que são identificados por seus nomes amigáveis. Para oferecer suporte a esse comportamento, o exemplo de código anterior deve ser modificado para gerar notificações de evento de áudio para o dispositivo selecionado. Duas modificações são necessárias. Primeiro, altere a definição do construtor para aceitar uma cadeia de caracteres de ID de ponto de extremidade como um parâmetro de entrada (no lugar dos parâmetros de fluxo e função no exemplo de código). Essa cadeia de caracteres identifica o dispositivo de ponto de extremidade de áudio que corresponde ao DirectSound selecionado ou ao dispositivo de forma de onda herdado. Em segundo lugar, substitua a chamada para o método IMMDeviceEnumerator::GetDefaultAudioEndpoint por uma chamada para o método IMMDeviceEnumerator::GetDevice. A chamada GetDevice usa a cadeia de caracteres de ID de ponto de extremidade como um parâmetro de entrada e cria uma instância do dispositivo de ponto de extremidade que é identificada pela cadeia de caracteres.

A técnica para obter a cadeia de caracteres de ID de ponto de extremidade para um dispositivo DirectSound ou dispositivo de forma de onda herdado é a seguinte.

Primeiro, durante a enumeração do dispositivo, o DirectSound fornece a cadeia de caracteres de ID do ponto de extremidade para cada dispositivo enumerado. Para iniciar a enumeração de dispositivos, o aplicativo passa um ponteiro de função de retorno de chamada como um parâmetro de entrada para a função DirectSoundCreate ou DirectSoundCaptureCreate . A definição da função de retorno de chamada é:

BOOL DSEnumCallback(
  LPGUID  lpGuid,
  LPCSTR  lpcstrDescription,
  LPCSTR  lpcstrModule,
  LPVOID  lpContext
);

No Windows Vista, o parâmetro lpcstrModule aponta para a cadeia de caracteres de ID do ponto de extremidade. (Em versões anteriores do Windows, incluindo Windows Server 2003, Windows XP e Windows 2000, o parâmetro lpcstrModule aponta para o nome do módulo de driver do dispositivo.) O parâmetro lpcstrDescription aponta para uma cadeia de caracteres que contém o nome amigável do dispositivo. Para obter mais informações sobre a enumeração de dispositivos DirectSound, consulte a documentação do SDK do Windows.

Em segundo lugar, para obter a cadeia de caracteres de ID de ponto de extremidade para um dispositivo de forma de onda herdado, use a função waveOutMessage ou waveInMessage para enviar uma mensagem de DRV_QUERYFUNCTIONINSTANCEID para o driver de dispositivo de forma de onda. Para obter um exemplo de código que mostra o uso dessa mensagem, consulte Funções de dispositivo para aplicativos multimídia herdados do Windows.

Interoperabilidade com APIs de áudio herdadas