Eventi audio per applicazioni audio legacy

Le API audio legacy, ad esempio DirectSound, DirectShow e le funzioni waveOutXxx , consentono alle applicazioni di ottenere e impostare i livelli di volume dei flussi audio. Le applicazioni possono usare le funzionalità di controllo del volume in queste API per visualizzare i dispositivi di scorrimento del volume nelle finestre delle applicazioni.

In Windows Vista, il programma di controllo del volume di sistema, Sndvol, consente agli utenti di controllare i livelli di volume audio per singole applicazioni. I dispositivi di scorrimento del volume visualizzati dalle applicazioni devono essere collegati ai dispositivi di scorrimento del volume corrispondenti in Sndvol. Se un utente regola il volume dell'applicazione tramite un dispositivo di scorrimento del volume in una finestra dell'applicazione, il dispositivo di scorrimento del volume corrispondente in Sndvol si sposta immediatamente per indicare il nuovo livello di volume. Viceversa, se l'utente regola il volume dell'applicazione tramite Sndvol, i dispositivi di scorrimento del volume nella finestra dell'applicazione devono essere spostati per indicare il nuovo livello di volume.

In Windows Vista Sndvol riflette immediatamente le modifiche apportate da un'applicazione tramite chiamate al metodo IDirectSoundBuffer::SetVolume o alla funzione waveOutSetVolume . Tuttavia, un'API audio legacy, ad esempio DirectSound o le funzioni waveOutXxx , non consente di notificare a un'applicazione quando l'utente modifica il volume dell'applicazione tramite Sndvol. Se un'applicazione visualizza un dispositivo di scorrimento del volume ma non riceve notifiche di modifiche al volume, il dispositivo di scorrimento non rifletterà le modifiche apportate dall'utente in Sndvol. Per implementare il comportamento appropriato, la finestra di progettazione dell'applicazione deve in qualche modo compensare la mancanza di notifiche dall'API audio legacy.

Una soluzione potrebbe essere che l'applicazione imposti un timer per ricordarlo periodicamente per controllare il livello del volume per verificare se è stato modificato.

Una soluzione più elegante consiste nell'usare le funzionalità di notifica degli eventi delle API audio principali. In particolare, l'applicazione può registrare un'interfaccia IAudioSessionEvents per ricevere callback quando si verificano modifiche del volume o altri tipi di eventi audio. Quando il volume cambia, la routine di callback di modifica del volume può aggiornare immediatamente il dispositivo di scorrimento del volume dell'applicazione in modo da riflettere la modifica.

L'esempio di codice seguente mostra come un'applicazione può registrarsi per ricevere notifiche di modifiche al volume e altri eventi audio:

//-----------------------------------------------------------
// 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)
};

L'esempio di codice precedente implementa una classe denominata AudioVolumeEvents. Durante l'inizializzazione del programma, l'applicazione audio abilita le notifiche degli eventi audio creando un oggetto AudioVolumeEvents. Il costruttore per questa classe accetta tre parametri di input:

Il costruttore fornisce i valori di flusso e ruolo come parametri di input al metodo IMMDeviceEnumerator::GetDefaultAudioEndpoint. Il metodo crea un oggetto IMMDevice che incapsula il dispositivo endpoint audio con la direzione del flusso di dati e il ruolo dispositivo specificati.

L'applicazione implementa l'oggetto a cui punta pAudioEvents. L'implementazione non è illustrata nell'esempio di codice precedente. Per un esempio di codice che implementa un'interfaccia IAudioSessionEvents , vedi Eventi sessione audio. Ogni metodo in questa interfaccia riceve notifiche di un particolare tipo di evento audio. Se l'applicazione non è interessata a un particolare tipo di evento, il metodo per tale tipo di evento non deve eseguire alcuna operazione ma restituire S_OK.

Il metodo IAudioSessionEvents::OnSimpleVolumeChanged riceve notifiche di modifiche al volume. In genere, questo metodo aggiorna il dispositivo di scorrimento del volume dell'applicazione.

Nell'esempio di codice precedente il costruttore per la classe AudioVolumeEvents registra le notifiche nella sessione audio specifica del processo identificata dal valore GUID della sessione GUID_NULL. Per impostazione predefinita, le API audio legacy, ad esempio DirectSound, DirectShow e le funzioni waveOutXxx assegnano i flussi a questa sessione. Tuttavia, un'applicazione DirectSound o DirectShow può, come opzione, eseguire l'override del comportamento predefinito e assegnarne i flussi a una sessione tra processi o a una sessione identificata da un valore GUID diverso da GUID_NULL. Non è attualmente disponibile alcun meccanismo per un'applicazione waveOutXxx per eseguire l'override del comportamento predefinito in modo simile. Per un esempio di codice di un'applicazione DirectShow con questo comportamento, vedere Ruoli dispositivo per applicazioni DirectShow. Per supportare tale applicazione, è possibile modificare il costruttore nell'esempio di codice precedente per accettare due parametri di input aggiuntivi, ovvero un GUID di sessione e un flag per indicare se la sessione da monitorare è una sessione specifica del processo o del processo. Passare questi parametri alla chiamata al metodo IAudioSessionManager::GetAudioSessionControl nel costruttore.

Dopo che il costruttore chiama il metodo IAudioSessionControl::RegisterAudioSessionNotification per la registrazione per le notifiche, l'applicazione continua a ricevere notifiche solo finché esiste l'interfaccia IAudioSessionControl o IAudioSessionManager. L'oggetto AudioVolumeEvents nell'esempio di codice precedente contiene riferimenti a queste interfacce fino a quando non viene chiamato il relativo distruttore. Questo comportamento garantisce che l'applicazione continui a ricevere notifiche per la durata dell'oggetto AudioVolumeEvents.

Anziché selezionare in modo implicito un dispositivo audio in base al ruolo del dispositivo, un'applicazione multimediale Windows DirectSound o legacy potrebbe consentire all'utente di selezionare in modo esplicito un dispositivo da un elenco di dispositivi disponibili identificati dai nomi descrittivi. Per supportare questo comportamento, è necessario modificare l'esempio di codice precedente per generare notifiche degli eventi audio per il dispositivo selezionato. Sono necessarie due modifiche. Prima di tutto, modificare la definizione del costruttore in modo da accettare una stringa ID endpoint come parametro di input (al posto del flusso e dei parametri del ruolo nell'esempio di codice). Questa stringa identifica il dispositivo endpoint audio che corrisponde al dispositivo DirectSound o waveform legacy selezionato. In secondo luogo, sostituire la chiamata al metodo IMMDeviceEnumerator::GetDefaultAudioEndpoint con una chiamata al metodo IMMDeviceEnumerator::GetDevice . La chiamata GetDevice accetta la stringa id endpoint come parametro di input e crea un'istanza del dispositivo endpoint identificato dalla stringa.

La tecnica per ottenere la stringa id endpoint per un dispositivo DirectSound o un dispositivo waveform legacy è la seguente.

Prima di tutto, durante l'enumerazione del dispositivo DirectSound fornisce la stringa ID endpoint per ogni dispositivo enumerato. Per iniziare l'enumerazione del dispositivo, l'applicazione passa un puntatore di funzione di callback come parametro di input alla funzione DirectSoundCreate o DirectSoundCaptureCreate . La definizione della funzione di callback è:

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

In Windows Vista il parametro lpcstrModule punta alla stringa id endpoint. Nelle versioni precedenti di Windows, tra cui Windows Server 2003, Windows XP e Windows 2000, il parametro lpcstrModule punta al nome del modulo driver per il dispositivo. Il parametro lpcstrDescription punta a una stringa contenente il nome descrittivo del dispositivo. Per altre informazioni sull'enumerazione dei dispositivi DirectSound, vedere la documentazione di Windows SDK.

In secondo luogo, per ottenere la stringa id endpoint per un dispositivo waveform legacy, usare la funzione waveOutMessage o waveInMessage per inviare un messaggio DRV_QUERYFUNCTIONINSTANCEID al driver di dispositivo waveform. Per un esempio di codice che mostra l'uso di questo messaggio, vedere Ruoli del dispositivo per applicazioni multimediali Windows legacy.

Interoperabilità con le API audio legacy