Звуковые события для устаревших звуковых приложений

Устаревшие API аудио, такие как DirectSound, DirectShow и функции waveOutXxx , позволяют приложениям получать и задавать уровни громкости аудиопотоков. Приложения могут использовать возможности управления томами в этих API для отображения ползунков томов в окнах приложений.

В Windows Vista программа системного управления громкостями Sndvol позволяет пользователям управлять уровнями громкости звука для отдельных приложений. Ползунки тома, отображаемые приложениями, должны быть связаны с соответствующими ползунками томов в Sndvol. Если пользователь настраивает том приложения через ползунок тома в окне приложения, соответствующий ползунок тома в Sndvol немедленно перемещается, чтобы указать новый уровень тома. И наоборот, если пользователь настраивает том приложения через Sndvol, то ползунки тома в окне приложения должны перемещаться, чтобы указать новый уровень тома.

В Windows Vista Sndvol немедленно отражает изменения тома, которые приложение выполняет с помощью вызовов метода IDirectSoundBuffer::SetVolume или функции waveOutSetVolume . Однако устаревший API аудио, например DirectSound или функции waveOutXxx , не позволяют уведомлять приложение, когда пользователь изменяет том приложения через Sndvol. Если приложение отображает ползунок тома, но не получает уведомления об изменениях тома, ползунок не будет отражать изменения, внесенные пользователем в Sndvol. Чтобы реализовать соответствующее поведение, конструктор приложений должен каким-то образом компенсировать отсутствие уведомлений от устаревшего API аудио.

Одно решение может быть для приложения, чтобы задать таймер, чтобы периодически напоминать ему, чтобы проверка уровне тома, чтобы узнать, изменилось ли оно.

Более элегантное решение предназначено для приложения для использования возможностей уведомлений о событиях основных API аудио. В частности, приложение может зарегистрировать интерфейс IAudioSessionEvents для получения обратных вызовов при изменении громкости или других типах звуковых событий. При изменении тома подпрограмма обратного вызова изменения тома может немедленно обновить ползунок тома приложения, чтобы отразить это изменение.

В следующем примере кода показано, как приложение может регистрироваться для получения уведомлений об изменениях тома и других звуковых событиях:

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

В предыдущем примере кода реализован класс AudioVolumeEvents. Во время инициализации программы звуковое приложение включает уведомления о событиях аудио, создавая объект AudioVolumeEvents. Конструктор для этого класса принимает три входных параметра:

Конструктор предоставляет значения потока и роли в качестве входных параметров методу IMMDeviceEnumerator::GetDefaultAudioEndpoint . Метод создает объект IMMDevice , который инкапсулирует устройство аудио конечной точки с указанным направлением потока данных и ролью устройства.

Приложение реализует объект, на который указывает pAudioEvents. (Реализация не показана в предыдущем примере кода. Пример кода, реализующий интерфейс IAudioSessionEvents , см. в разделе "События сеанса аудио".) Каждый метод в этом интерфейсе получает уведомления о конкретном типе звукового события. Если приложение не заинтересовано в определенном типе события, метод для этого типа события не должен ничего делать, кроме возврата S_OK.

Метод IAudioSessionEvents::OnSimpleVolumeChanged получает уведомления об изменениях тома. Как правило, этот метод обновляет ползунок тома приложения.

В приведенном выше примере кода конструктор класса AudioVolumeEvents регистрирует уведомления в сеансе аудио, определяемом значением GUID сеанса, который определяется значением GUID сеанса GUID_NULL. По умолчанию устаревшие API аудио, такие как DirectSound, DirectShow и функции waveOutXxx, назначают потоки этому сеансу. Однако приложение DirectSound или DirectShow может переопределить поведение по умолчанию и назначить потоки сеансу перекрестного процесса или сеансу, который определяется значением GUID, отличном от GUID_NULL. (В настоящее время для приложения waveOutXxx нет механизма для переопределения поведения по умолчанию аналогичным образом.) Пример кода приложения DirectShow с этим поведением см. в разделе "Роли устройств для приложений DirectShow". Для размещения такого приложения можно изменить конструктор в предыдущем примере кода, чтобы принять два дополнительных входных параметра — GUID сеанса и флаг, чтобы указать, является ли сеанс, который требуется отслеживать, является ли сеанс межпроцессным или конкретным процессом. Передайте эти параметры вызову метода IAudioSessionManager::GetAudioSessionControl в конструкторе.

После вызова метода IAudioSessionControl::RegisterAudioSessionNotification для регистрации уведомлений приложение продолжает получать уведомления только до тех пор, пока существует интерфейс IAudioSessionControl или IAudioSessionManager. Объект AudioVolumeEvents в предыдущем примере кода содержит ссылки на эти интерфейсы до вызова деструктора. Это поведение гарантирует, что приложение будет продолжать получать уведомления о времени существования объекта AudioVolumeEvents.

Вместо неявного выбора звукового устройства на основе его роли устройства directSound или устаревшего мультимедийного приложения Windows пользователь может явно выбрать устройство из списка доступных устройств, которые определяются их понятными именами. Для поддержки этого поведения предыдущий пример кода должен быть изменен для создания уведомлений аудио-событий для выбранного устройства. Требуются два изменения. Сначала измените определение конструктора, чтобы принять строку идентификатора конечной точки в качестве входного параметра (вместо параметров потока и ролей в примере кода). Эта строка определяет устройство аудио конечной точки, соответствующее выбранному устройству DirectSound или устаревшей волнообразной форме. Во-вторых, замените вызов метода IMMDeviceEnumerator::GetDefaultAudioEndpoint вызовом метода IMMDeviceEnumerator::GetDevice. Вызов GetDevice принимает строку идентификатора конечной точки в качестве входного параметра и создает экземпляр устройства конечной точки, определяемого строкой.

Ниже приведен способ получения строки идентификатора конечной точки для устройства DirectSound или устаревшего устройства волны.

Во-первых, во время перечисления устройств DirectSound предоставляет строку идентификатора конечной точки для каждого перечисленного устройства. Чтобы начать перечисление устройств, приложение передает указатель функции обратного вызова в качестве входного параметра функции DirectSoundCreate или DirectSoundCaptureCreate . Определение функции обратного вызова:

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

В Windows Vista параметр lpcstrModule указывает на строку идентификатора конечной точки. (В более ранних версиях Windows, включая Windows Server 2003, Windows XP и Windows 2000, параметр lpcstrModule указывает на имя модуля драйвера для устройства.) Параметр lpcstrDescription указывает на строку, содержащую понятное имя устройства. Дополнительные сведения о перечислении устройств DirectSound см. в документации по пакету SDK для Windows.

Во-вторых, чтобы получить строку идентификатора конечной точки для устаревшего устройства волны, используйте функцию waveOutMessage или waveInMessage для отправки DRV_QUERYFUNCTIONINSTANCEID сообщения драйверу устройства волны. Пример кода, показывающий использование этого сообщения, см. в разделе "Роли устройств" для устаревших приложений мультимедиа Windows.

Взаимодействие с устаревшими API аудио